Commit 579a7003 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power supply and reset updates from Sebastian Reichel:

 - New core support:
    - battery internal resistance
    - battery OCV capacity lookup table
    - support for custom sysfs attributes

 - Convert all drivers to use power-supply core support for custom sysfs
   attributes

 - bq24190-charger: bq24196 support

 - axp20x-charger: AXP813 support

 - sc27xx-battery: new fuel gauge driver

 - gpio-poweroff: support for specific active and inactive delays

 - Misc fixes

* tag 'for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (53 commits)
  power: supply: bq25890: fix BAT_COMP field definition
  power: supply: gpio-charger: Do not use deprecated POWER_SUPPLY_TYPE_USB_*
  power: supply: ds2781: switch to devm_power_supply_register
  power: supply: ds2780: switch to devm_power_supply_register
  power: supply: ds2781: fix race-condition in bin attribute registration
  power: supply: ds2780: fix race-condition in bin attribute registration
  power: supply: pcf50633: fix race-condition in sysfs registration
  power: supply: charger-manager: fix race-condition in sysfs registration
  power: supply: charger-manager: simplify generation of sysfs attribute group name
  power: supply: bq24257: fix race-condition in sysfs registration
  power: supply: bq24190_charger: fix race-condition in sysfs registration
  power: supply: lp8788: fix race-condition in sysfs registration
  power: supply: ds2781: fix race-condition in sysfs registration
  power: supply: ds2780: fix race-condition in sysfs registration
  power: supply: bq2415x: fix race-condition in sysfs registration
  power: supply: core: add support for custom sysfs attributes
  power: supply: sc27xx: Save last battery capacity
  power: reset: at91-poweroff: move shdwc related data to one structure
  power: supply: sc27xx: Add suspend/resume interfaces
  power: supply: sc27xx: Add fuel gauge low voltage alarm
  ...
parents 030672ae 95809139
...@@ -27,6 +27,8 @@ Optional properties: ...@@ -27,6 +27,8 @@ Optional properties:
it to an output when the power-off handler is called. If this optional it to an output when the power-off handler is called. If this optional
property is not specified, the GPIO is initialized as an output in its property is not specified, the GPIO is initialized as an output in its
inactive state. inactive state.
- active-delay-ms: Delay (default 100) to wait after driving gpio active
- inactive-delay-ms: Delay (default 100) to wait after driving gpio inactive
- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is - timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
specified, 3000 ms is used. specified, 3000 ms is used.
......
...@@ -4,6 +4,7 @@ Required Properties: ...@@ -4,6 +4,7 @@ Required Properties:
- compatible: One of: - compatible: One of:
"x-powers,axp202-ac-power-supply" "x-powers,axp202-ac-power-supply"
"x-powers,axp221-ac-power-supply" "x-powers,axp221-ac-power-supply"
"x-powers,axp813-ac-power-supply"
This node is a subnode of the axp20x PMIC. This node is a subnode of the axp20x PMIC.
...@@ -13,6 +14,8 @@ reading ADC channels from the AXP20X ADC. ...@@ -13,6 +14,8 @@ reading ADC channels from the AXP20X ADC.
The AXP22X is only able to tell if an AC power supply is present and The AXP22X is only able to tell if an AC power supply is present and
usable. usable.
AXP813/AXP803 are able to limit current and supply voltage
Example: Example:
&axp209 { &axp209 {
......
...@@ -22,6 +22,18 @@ Optional Properties: ...@@ -22,6 +22,18 @@ Optional Properties:
- charge-term-current-microamp: current for charge termination phase - charge-term-current-microamp: current for charge termination phase
- constant-charge-current-max-microamp: maximum constant input current - constant-charge-current-max-microamp: maximum constant input current
- constant-charge-voltage-max-microvolt: maximum constant input voltage - constant-charge-voltage-max-microvolt: maximum constant input voltage
- factory-internal-resistance-micro-ohms: battery factory internal resistance
- ocv-capacity-table-0: An array providing the open circuit voltage (OCV)
of the battery and corresponding battery capacity percent, which is used
to look up battery capacity according to current OCV value. And the open
circuit voltage unit is microvolt.
- ocv-capacity-table-1: Same as ocv-capacity-table-0
......
- ocv-capacity-table-n: Same as ocv-capacity-table-0
- ocv-capacity-celsius: An array containing the temperature in degree Celsius,
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.
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
...@@ -42,6 +54,11 @@ Example: ...@@ -42,6 +54,11 @@ Example:
charge-term-current-microamp = <128000>; charge-term-current-microamp = <128000>;
constant-charge-current-max-microamp = <900000>; constant-charge-current-max-microamp = <900000>;
constant-charge-voltage-max-microvolt = <4200000>; constant-charge-voltage-max-microvolt = <4200000>;
factory-internal-resistance-micro-ohms = <250000>;
ocv-capacity-celsius = <(-10) 0 10>;
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>, ...;
}; };
charger: charger@11 { charger: charger@11 {
......
...@@ -3,7 +3,9 @@ TI BQ24190 Li-Ion Battery Charger ...@@ -3,7 +3,9 @@ TI BQ24190 Li-Ion Battery Charger
Required properties: Required properties:
- compatible: contains one of the following: - compatible: contains one of the following:
* "ti,bq24190" * "ti,bq24190"
* "ti,bq24192"
* "ti,bq24192i" * "ti,bq24192i"
* "ti,bq24196"
- reg: integer, I2C address of the charger. - reg: integer, I2C address of the charger.
- interrupts[-extended]: configuration for charger INT pin. - interrupts[-extended]: configuration for charger INT pin.
...@@ -19,6 +21,12 @@ Optional properties: ...@@ -19,6 +21,12 @@ Optional properties:
- ti,system-minimum-microvolt: when power is connected and the battery is below - ti,system-minimum-microvolt: when power is connected and the battery is below
minimum system voltage, the system will be regulated above this setting. minimum system voltage, the system will be regulated above this setting.
child nodes:
- usb-otg-vbus:
Usage: optional
Description: Regulator that is used to control the VBUS voltage direction for
either USB host mode or for charging on the OTG port.
Notes: Notes:
- Some circuit boards wire the chip's "OTG" pin high (enabling 500mA default - Some circuit boards wire the chip's "OTG" pin high (enabling 500mA default
charge current on USB SDP ports, among other features). To simulate this on charge current on USB SDP ports, among other features). To simulate this on
...@@ -39,6 +47,8 @@ Example: ...@@ -39,6 +47,8 @@ Example:
interrupts-extended = <&gpiochip 10 IRQ_TYPE_EDGE_FALLING>; interrupts-extended = <&gpiochip 10 IRQ_TYPE_EDGE_FALLING>;
monitored-battery = <&bat>; monitored-battery = <&bat>;
ti,system-minimum-microvolt = <3200000>; ti,system-minimum-microvolt = <3200000>;
usb_otg_vbus: usb-otg-vbus { };
}; };
&twl_gpio { &twl_gpio {
......
Spreadtrum SC27XX PMICs Fuel Gauge Unit Power Supply Bindings
Required properties:
- compatible: Should be one of the following:
"sprd,sc2720-fgu",
"sprd,sc2721-fgu",
"sprd,sc2723-fgu",
"sprd,sc2730-fgu",
"sprd,sc2731-fgu".
- reg: The address offset of fuel gauge unit.
- battery-detect-gpios: GPIO for battery detection.
- io-channels: Specify the IIO ADC channel to get temperature.
- io-channel-names: Should be "bat-temp".
- nvmem-cells: A phandle to the calibration cells provided by eFuse device.
- nvmem-cell-names: Should be "fgu_calib".
- monitored-battery: Phandle of battery characteristics devicetree node.
See Documentation/devicetree/bindings/power/supply/battery.txt
Example:
bat: battery {
compatible = "simple-battery";
charge-full-design-microamp-hours = <1900000>;
constant-charge-voltage-max-microvolt = <4350000>;
ocv-capacity-celsius = <20>;
ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>,
<4022000 85>, <3983000 80>, <3949000 75>,
<3917000 70>, <3889000 65>, <3864000 60>,
<3835000 55>, <3805000 50>, <3787000 45>,
<3777000 40>, <3773000 35>, <3770000 30>,
<3765000 25>, <3752000 20>, <3724000 15>,
<3680000 10>, <3605000 5>, <3400000 0>;
......
};
sc2731_pmic: pmic@0 {
compatible = "sprd,sc2731";
reg = <0>;
spi-max-frequency = <26000000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;
fgu@a00 {
compatible = "sprd,sc2731-fgu";
reg = <0xa00>;
battery-detect-gpios = <&pmic_eic 9 GPIO_ACTIVE_HIGH>;
io-channels = <&pmic_adc 5>;
io-channel-names = "bat-temp";
nvmem-cells = <&fgu_calib>;
nvmem-cell-names = "fgu_calib";
monitored-battery = <&bat>;
};
};
...@@ -51,14 +51,16 @@ static const char *shdwc_wakeup_modes[] = { ...@@ -51,14 +51,16 @@ static const char *shdwc_wakeup_modes[] = {
[AT91_SHDW_WKMODE0_ANYLEVEL] = "any", [AT91_SHDW_WKMODE0_ANYLEVEL] = "any",
}; };
static void __iomem *at91_shdwc_base; static struct shdwc {
static struct clk *sclk; struct clk *sclk;
static void __iomem *mpddrc_base; void __iomem *shdwc_base;
void __iomem *mpddrc_base;
} at91_shdwc;
static void __init at91_wakeup_status(struct platform_device *pdev) static void __init at91_wakeup_status(struct platform_device *pdev)
{ {
const char *reason; const char *reason;
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR); u32 reg = readl(at91_shdwc.shdwc_base + AT91_SHDW_SR);
/* Simple power-on, just bail out */ /* Simple power-on, just bail out */
if (!reg) if (!reg)
...@@ -75,11 +77,6 @@ static void __init at91_wakeup_status(struct platform_device *pdev) ...@@ -75,11 +77,6 @@ static void __init at91_wakeup_status(struct platform_device *pdev)
} }
static void at91_poweroff(void) static void at91_poweroff(void)
{
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
static void at91_lpddr_poweroff(void)
{ {
asm volatile( asm volatile(
/* Align to cache lines */ /* Align to cache lines */
...@@ -89,15 +86,17 @@ static void at91_lpddr_poweroff(void) ...@@ -89,15 +86,17 @@ static void at91_lpddr_poweroff(void)
" ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
/* Power down SDRAM0 */ /* Power down SDRAM0 */
" tst %0, #0\n\t"
" beq 1f\n\t"
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
/* Shutdown CPU */ /* Shutdown CPU */
" str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" "1: str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
" b .\n\t" " b .\n\t"
: :
: "r" (mpddrc_base), : "r" (at91_shdwc.mpddrc_base),
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
"r" (at91_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)
: "r6"); : "r6");
} }
...@@ -147,7 +146,7 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) ...@@ -147,7 +146,7 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
mode |= AT91_SHDW_RTTWKEN; mode |= AT91_SHDW_RTTWKEN;
writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR); writel(wakeup_mode | mode, at91_shdwc.shdwc_base + AT91_SHDW_MR);
} }
static int __init at91_poweroff_probe(struct platform_device *pdev) static int __init at91_poweroff_probe(struct platform_device *pdev)
...@@ -158,15 +157,15 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) ...@@ -158,15 +157,15 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
int ret; int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); at91_shdwc.shdwc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_shdwc_base)) if (IS_ERR(at91_shdwc.shdwc_base))
return PTR_ERR(at91_shdwc_base); return PTR_ERR(at91_shdwc.shdwc_base);
sclk = devm_clk_get(&pdev->dev, NULL); at91_shdwc.sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk)) if (IS_ERR(at91_shdwc.sclk))
return PTR_ERR(sclk); return PTR_ERR(at91_shdwc.sclk);
ret = clk_prepare_enable(sclk); ret = clk_prepare_enable(at91_shdwc.sclk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Could not enable slow clock\n"); dev_err(&pdev->dev, "Could not enable slow clock\n");
return ret; return ret;
...@@ -177,44 +176,47 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) ...@@ -177,44 +176,47 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
if (pdev->dev.of_node) if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev); at91_poweroff_dt_set_wakeup_mode(pdev);
pm_power_off = at91_poweroff;
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
if (!np) if (np) {
return 0; at91_shdwc.mpddrc_base = of_iomap(np, 0);
mpddrc_base = of_iomap(np, 0);
of_node_put(np); of_node_put(np);
if (!mpddrc_base) if (!at91_shdwc.mpddrc_base) {
return 0; ret = -ENOMEM;
goto clk_disable;
}
ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; ddr_type = readl(at91_shdwc.mpddrc_base + AT91_DDRSDRC_MDR) &
if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || AT91_DDRSDRC_MD;
(ddr_type == AT91_DDRSDRC_MD_LPDDR3)) if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 &&
pm_power_off = at91_lpddr_poweroff; ddr_type != AT91_DDRSDRC_MD_LPDDR3) {
else iounmap(at91_shdwc.mpddrc_base);
iounmap(mpddrc_base); at91_shdwc.mpddrc_base = NULL;
}
}
pm_power_off = at91_poweroff;
return 0; return 0;
clk_disable:
clk_disable_unprepare(at91_shdwc.sclk);
return ret;
} }
static int __exit at91_poweroff_remove(struct platform_device *pdev) static int __exit at91_poweroff_remove(struct platform_device *pdev)
{ {
if (pm_power_off == at91_poweroff || if (pm_power_off == at91_poweroff)
pm_power_off == at91_lpddr_poweroff)
pm_power_off = NULL; pm_power_off = NULL;
clk_disable_unprepare(sclk); if (at91_shdwc.mpddrc_base)
iounmap(at91_shdwc.mpddrc_base);
clk_disable_unprepare(at91_shdwc.sclk);
return 0; return 0;
} }
static const struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,sama5d3-ddramc", },
{ /* sentinel */ }
};
static const struct of_device_id at91_poweroff_of_match[] = { static const struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", }, { .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", }, { .compatible = "atmel,at91sam9rl-shdwc", },
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
*/ */
static struct gpio_desc *reset_gpio; static struct gpio_desc *reset_gpio;
static u32 timeout = DEFAULT_TIMEOUT_MS; static u32 timeout = DEFAULT_TIMEOUT_MS;
static u32 active_delay = 100;
static u32 inactive_delay = 100;
static void gpio_poweroff_do_poweroff(void) static void gpio_poweroff_do_poweroff(void)
{ {
...@@ -33,10 +35,11 @@ static void gpio_poweroff_do_poweroff(void) ...@@ -33,10 +35,11 @@ static void gpio_poweroff_do_poweroff(void)
/* drive it active, also inactive->active edge */ /* drive it active, also inactive->active edge */
gpiod_direction_output(reset_gpio, 1); gpiod_direction_output(reset_gpio, 1);
mdelay(100); mdelay(active_delay);
/* drive inactive, also active->inactive edge */ /* drive inactive, also active->inactive edge */
gpiod_set_value_cansleep(reset_gpio, 0); gpiod_set_value_cansleep(reset_gpio, 0);
mdelay(100); mdelay(inactive_delay);
/* drive it active, also inactive->active edge */ /* drive it active, also inactive->active edge */
gpiod_set_value_cansleep(reset_gpio, 1); gpiod_set_value_cansleep(reset_gpio, 1);
...@@ -66,6 +69,9 @@ static int gpio_poweroff_probe(struct platform_device *pdev) ...@@ -66,6 +69,9 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
else else
flags = GPIOD_OUT_LOW; flags = GPIOD_OUT_LOW;
device_property_read_u32(&pdev->dev, "active-delay-ms", &active_delay);
device_property_read_u32(&pdev->dev, "inactive-delay-ms",
&inactive_delay);
device_property_read_u32(&pdev->dev, "timeout-ms", &timeout); device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags); reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
......
...@@ -26,6 +26,13 @@ struct ocelot_reset_context { ...@@ -26,6 +26,13 @@ struct ocelot_reset_context {
#define SOFT_CHIP_RST BIT(0) #define SOFT_CHIP_RST BIT(0)
#define ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
#define IF_SI_OWNER_MASK GENMASK(1, 0)
#define IF_SI_OWNER_SISL 0
#define IF_SI_OWNER_SIBM 1
#define IF_SI_OWNER_SIMC 2
#define IF_SI_OWNER_OFFSET 4
static int ocelot_restart_handle(struct notifier_block *this, static int ocelot_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd) unsigned long mode, void *cmd)
{ {
...@@ -37,6 +44,11 @@ static int ocelot_restart_handle(struct notifier_block *this, ...@@ -37,6 +44,11 @@ static int ocelot_restart_handle(struct notifier_block *this,
regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET, regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET,
CORE_RST_PROTECT, 0); CORE_RST_PROTECT, 0);
/* Make the SI back to boot mode */
regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL,
IF_SI_OWNER_MASK << IF_SI_OWNER_OFFSET,
IF_SI_OWNER_SIBM << IF_SI_OWNER_OFFSET);
writel(SOFT_CHIP_RST, ctx->base); writel(SOFT_CHIP_RST, ctx->base);
pr_emerg("Unable to restart system\n"); pr_emerg("Unable to restart system\n");
......
...@@ -652,4 +652,12 @@ config CHARGER_SC2731 ...@@ -652,4 +652,12 @@ config CHARGER_SC2731
Say Y here to enable support for battery charging with SC2731 Say Y here to enable support for battery charging with SC2731
PMIC chips. PMIC chips.
config FUEL_GAUGE_SC27XX
tristate "Spreadtrum SC27XX fuel gauge driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
depends on IIO
help
Say Y here to enable support for fuel gauge with SC27XX
PMIC chips.
endif # POWER_SUPPLY endif # POWER_SUPPLY
...@@ -86,3 +86,4 @@ obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o ...@@ -86,3 +86,4 @@ obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
...@@ -27,6 +27,16 @@ ...@@ -27,6 +27,16 @@
#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_VHOLD_MASK GENMASK(5, 3)
#define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3)
#define AXP813_VHOLD_REG_TO_UV(x) \
(((((x) & AXP813_VHOLD_MASK) >> 3) + 40) * 100000)
#define AXP813_CURR_LIMIT_MASK GENMASK(2, 0)
#define AXP813_CURR_LIMIT_UA_TO_BIT(x) (((x) / 500000) - 3)
#define AXP813_CURR_LIMIT_REG_TO_UA(x) \
((((x) & AXP813_CURR_LIMIT_MASK) + 3) * 500000)
#define DRVNAME "axp20x-ac-power-supply" #define DRVNAME "axp20x-ac-power-supply"
struct axp20x_ac_power { struct axp20x_ac_power {
...@@ -102,6 +112,57 @@ static int axp20x_ac_power_get_property(struct power_supply *psy, ...@@ -102,6 +112,57 @@ static int axp20x_ac_power_get_property(struct power_supply *psy,
return 0; return 0;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
if (ret)
return ret;
val->intval = AXP813_VHOLD_REG_TO_UV(reg);
return 0;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
if (ret)
return ret;
val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg);
/* AXP813 datasheet defines values 11x as 4000mA */
if (val->intval > 4000000)
val->intval = 4000000;
return 0;
default:
return -EINVAL;
}
return -EINVAL;
}
static int axp813_ac_power_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
if (val->intval < 4000000 || val->intval > 4700000)
return -EINVAL;
return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
AXP813_VHOLD_MASK,
AXP813_VHOLD_UV_TO_BIT(val->intval));
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
if (val->intval < 1500000 || val->intval > 4000000)
return -EINVAL;
return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
AXP813_CURR_LIMIT_MASK,
AXP813_CURR_LIMIT_UA_TO_BIT(val->intval));
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -109,6 +170,13 @@ static int axp20x_ac_power_get_property(struct power_supply *psy, ...@@ -109,6 +170,13 @@ static int axp20x_ac_power_get_property(struct power_supply *psy,
return -EINVAL; return -EINVAL;
} }
static int axp813_ac_power_prop_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
}
static enum power_supply_property axp20x_ac_power_properties[] = { static enum power_supply_property axp20x_ac_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_PRESENT,
...@@ -123,6 +191,14 @@ static enum power_supply_property axp22x_ac_power_properties[] = { ...@@ -123,6 +191,14 @@ static enum power_supply_property axp22x_ac_power_properties[] = {
POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_ONLINE,
}; };
static enum power_supply_property axp813_ac_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
static const struct power_supply_desc axp20x_ac_power_desc = { static const struct power_supply_desc axp20x_ac_power_desc = {
.name = "axp20x-ac", .name = "axp20x-ac",
.type = POWER_SUPPLY_TYPE_MAINS, .type = POWER_SUPPLY_TYPE_MAINS,
...@@ -139,6 +215,16 @@ static const struct power_supply_desc axp22x_ac_power_desc = { ...@@ -139,6 +215,16 @@ static const struct power_supply_desc axp22x_ac_power_desc = {
.get_property = axp20x_ac_power_get_property, .get_property = axp20x_ac_power_get_property,
}; };
static const struct power_supply_desc axp813_ac_power_desc = {
.name = "axp813-ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = axp813_ac_power_properties,
.num_properties = ARRAY_SIZE(axp813_ac_power_properties),
.property_is_writeable = axp813_ac_power_prop_writeable,
.get_property = axp20x_ac_power_get_property,
.set_property = axp813_ac_power_set_property,
};
struct axp_data { struct axp_data {
const struct power_supply_desc *power_desc; const struct power_supply_desc *power_desc;
bool acin_adc; bool acin_adc;
...@@ -154,6 +240,11 @@ static const struct axp_data axp22x_data = { ...@@ -154,6 +240,11 @@ static const struct axp_data axp22x_data = {
.acin_adc = false, .acin_adc = false,
}; };
static const struct axp_data axp813_data = {
.power_desc = &axp813_ac_power_desc,
.acin_adc = false,
};
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);
...@@ -234,6 +325,9 @@ static const struct of_device_id axp20x_ac_power_match[] = { ...@@ -234,6 +325,9 @@ static const struct of_device_id axp20x_ac_power_match[] = {
}, { }, {
.compatible = "x-powers,axp221-ac-power-supply", .compatible = "x-powers,axp221-ac-power-supply",
.data = &axp22x_data, .data = &axp22x_data,
}, {
.compatible = "x-powers,axp813-ac-power-supply",
.data = &axp813_data,
}, { /* sentinel */ } }, { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* option) any later version. * option) any later version.
*/ */
#include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -29,17 +30,17 @@ ...@@ -29,17 +30,17 @@
#include <linux/mfd/axp20x.h> #include <linux/mfd/axp20x.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#define PS_STAT_VBUS_TRIGGER (1 << 0) #define PS_STAT_VBUS_TRIGGER BIT(0)
#define PS_STAT_BAT_CHRG_DIR (1 << 2) #define PS_STAT_BAT_CHRG_DIR BIT(2)
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) #define PS_STAT_VBAT_ABOVE_VHOLD BIT(3)
#define PS_STAT_VBUS_VALID (1 << 4) #define PS_STAT_VBUS_VALID BIT(4)
#define PS_STAT_VBUS_PRESENT (1 << 5) #define PS_STAT_VBUS_PRESENT BIT(5)
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) #define CHRG_STAT_BAT_SAFE_MODE BIT(3)
#define CHRG_STAT_BAT_VALID (1 << 4) #define CHRG_STAT_BAT_VALID BIT(4)
#define CHRG_STAT_BAT_PRESENT (1 << 5) #define CHRG_STAT_BAT_PRESENT BIT(5)
#define CHRG_STAT_CHARGING (1 << 6) #define CHRG_STAT_CHARGING BIT(6)
#define CHRG_STAT_PMIC_OTP (1 << 7) #define CHRG_STAT_PMIC_OTP BIT(7)
#define VBUS_ISPOUT_CUR_LIM_MASK 0x03 #define VBUS_ISPOUT_CUR_LIM_MASK 0x03
#define VBUS_ISPOUT_CUR_LIM_BIT_POS 0 #define VBUS_ISPOUT_CUR_LIM_BIT_POS 0
...@@ -52,33 +53,33 @@ ...@@ -52,33 +53,33 @@
#define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */ #define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */
#define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */ #define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */
#define VBUS_ISPOUT_VHOLD_SET_4300MV 0x3 /* 4300mV */ #define VBUS_ISPOUT_VHOLD_SET_4300MV 0x3 /* 4300mV */
#define VBUS_ISPOUT_VBUS_PATH_DIS (1 << 7) #define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7)
#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ #define CHRG_CCCV_CC_MASK 0xf /* 4 bits */
#define CHRG_CCCV_CC_BIT_POS 0 #define CHRG_CCCV_CC_BIT_POS 0
#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ #define CHRG_CCCV_CC_OFFSET 200 /* 200mA */
#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ #define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */ #define CHRG_CCCV_ITERM_20P BIT(4) /* 20% of CC */
#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ #define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */
#define CHRG_CCCV_CV_BIT_POS 5 #define CHRG_CCCV_CV_BIT_POS 5
#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ #define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */
#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ #define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */
#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ #define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ #define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
#define CHRG_CCCV_CHG_EN (1 << 7) #define CHRG_CCCV_CHG_EN BIT(7)
#define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */ #define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */
#define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */ #define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */
#define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */ #define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */
#define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */ #define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */
#define CNTL2_CHGLED_TYPEB (1 << 4) #define CNTL2_CHGLED_TYPEB BIT(4)
#define CNTL2_CHG_OUT_TURNON (1 << 5) #define CNTL2_CHG_OUT_TURNON BIT(5)
#define CNTL2_PC_TIMEOUT_MASK 0xC0 #define CNTL2_PC_TIMEOUT_MASK 0xC0
#define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */ #define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */
#define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */ #define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */
#define CNTL2_PC_TIMEOUT_70MINS 0x3 #define CNTL2_PC_TIMEOUT_70MINS 0x3
#define CHRG_ILIM_TEMP_LOOP_EN (1 << 3) #define CHRG_ILIM_TEMP_LOOP_EN BIT(3)
#define CHRG_VBUS_ILIM_MASK 0xf0 #define CHRG_VBUS_ILIM_MASK 0xf0
#define CHRG_VBUS_ILIM_BIT_POS 4 #define CHRG_VBUS_ILIM_BIT_POS 4
#define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */ #define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */
...@@ -94,7 +95,7 @@ ...@@ -94,7 +95,7 @@
#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */ #define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
#define CHRG_VHTFC_45C 0x1F /* 45 DegC */ #define CHRG_VHTFC_45C 0x1F /* 45 DegC */
#define FG_CNTL_OCV_ADJ_EN (1 << 3) #define FG_CNTL_OCV_ADJ_EN BIT(3)
#define CV_4100MV 4100 /* 4100mV */ #define CV_4100MV 4100 /* 4100mV */
#define CV_4150MV 4150 /* 4150mV */ #define CV_4150MV 4150 /* 4150mV */
......
...@@ -1032,54 +1032,6 @@ static int bq2415x_power_supply_get_property(struct power_supply *psy, ...@@ -1032,54 +1032,6 @@ static int bq2415x_power_supply_get_property(struct power_supply *psy,
return 0; return 0;
} }
static int bq2415x_power_supply_init(struct bq2415x_device *bq)
{
int ret;
int chip;
char revstr[8];
struct power_supply_config psy_cfg = {
.drv_data = bq,
.of_node = bq->dev->of_node,
};
bq->charger_desc.name = bq->name;
bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
bq->charger_desc.properties = bq2415x_power_supply_props;
bq->charger_desc.num_properties =
ARRAY_SIZE(bq2415x_power_supply_props);
bq->charger_desc.get_property = bq2415x_power_supply_get_property;
ret = bq2415x_detect_chip(bq);
if (ret < 0)
chip = BQUNKNOWN;
else
chip = ret;
ret = bq2415x_detect_revision(bq);
if (ret < 0)
strcpy(revstr, "unknown");
else
sprintf(revstr, "1.%d", ret);
bq->model = kasprintf(GFP_KERNEL,
"chip %s, revision %s, vender code %.3d",
bq2415x_chip_name[chip], revstr,
bq2415x_get_vender_code(bq));
if (!bq->model) {
dev_err(bq->dev, "failed to allocate model name\n");
return -ENOMEM;
}
bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
&psy_cfg);
if (IS_ERR(bq->charger)) {
kfree(bq->model);
return PTR_ERR(bq->charger);
}
return 0;
}
static void bq2415x_power_supply_exit(struct bq2415x_device *bq) static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
{ {
bq->autotimer = 0; bq->autotimer = 0;
...@@ -1496,7 +1448,7 @@ static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); ...@@ -1496,7 +1448,7 @@ static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static struct attribute *bq2415x_sysfs_attributes[] = { static struct attribute *bq2415x_sysfs_attrs[] = {
/* /*
* TODO: some (appropriate) of these attrs should be switched to * TODO: some (appropriate) of these attrs should be switched to
* use power supply class props. * use power supply class props.
...@@ -1525,19 +1477,55 @@ static struct attribute *bq2415x_sysfs_attributes[] = { ...@@ -1525,19 +1477,55 @@ static struct attribute *bq2415x_sysfs_attributes[] = {
NULL, NULL,
}; };
static const struct attribute_group bq2415x_sysfs_attr_group = { ATTRIBUTE_GROUPS(bq2415x_sysfs);
.attrs = bq2415x_sysfs_attributes,
};
static int bq2415x_sysfs_init(struct bq2415x_device *bq) static int bq2415x_power_supply_init(struct bq2415x_device *bq)
{ {
return sysfs_create_group(&bq->charger->dev.kobj, int ret;
&bq2415x_sysfs_attr_group); int chip;
} char revstr[8];
struct power_supply_config psy_cfg = {
.drv_data = bq,
.of_node = bq->dev->of_node,
.attr_grp = bq2415x_sysfs_groups,
};
static void bq2415x_sysfs_exit(struct bq2415x_device *bq) bq->charger_desc.name = bq->name;
{ bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
sysfs_remove_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group); bq->charger_desc.properties = bq2415x_power_supply_props;
bq->charger_desc.num_properties =
ARRAY_SIZE(bq2415x_power_supply_props);
bq->charger_desc.get_property = bq2415x_power_supply_get_property;
ret = bq2415x_detect_chip(bq);
if (ret < 0)
chip = BQUNKNOWN;
else
chip = ret;
ret = bq2415x_detect_revision(bq);
if (ret < 0)
strcpy(revstr, "unknown");
else
sprintf(revstr, "1.%d", ret);
bq->model = kasprintf(GFP_KERNEL,
"chip %s, revision %s, vender code %.3d",
bq2415x_chip_name[chip], revstr,
bq2415x_get_vender_code(bq));
if (!bq->model) {
dev_err(bq->dev, "failed to allocate model name\n");
return -ENOMEM;
}
bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
&psy_cfg);
if (IS_ERR(bq->charger)) {
kfree(bq->model);
return PTR_ERR(bq->charger);
}
return 0;
} }
/* main bq2415x probe function */ /* main bq2415x probe function */
...@@ -1651,16 +1639,10 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1651,16 +1639,10 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_2; goto error_2;
} }
ret = bq2415x_sysfs_init(bq);
if (ret) {
dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
goto error_3;
}
ret = bq2415x_set_defaults(bq); ret = bq2415x_set_defaults(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to set default values: %d\n", ret); dev_err(bq->dev, "failed to set default values: %d\n", ret);
goto error_4; goto error_3;
} }
if (bq->notify_node || bq->init_data.notify_device) { if (bq->notify_node || bq->init_data.notify_device) {
...@@ -1668,7 +1650,7 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1668,7 +1650,7 @@ static int bq2415x_probe(struct i2c_client *client,
ret = power_supply_reg_notifier(&bq->nb); ret = power_supply_reg_notifier(&bq->nb);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to reg notifier: %d\n", ret); dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
goto error_4; goto error_3;
} }
bq->automode = 1; bq->automode = 1;
...@@ -1707,8 +1689,6 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1707,8 +1689,6 @@ static int bq2415x_probe(struct i2c_client *client,
dev_info(bq->dev, "driver registered\n"); dev_info(bq->dev, "driver registered\n");
return 0; return 0;
error_4:
bq2415x_sysfs_exit(bq);
error_3: error_3:
bq2415x_power_supply_exit(bq); bq2415x_power_supply_exit(bq);
error_2: error_2:
...@@ -1733,7 +1713,6 @@ static int bq2415x_remove(struct i2c_client *client) ...@@ -1733,7 +1713,6 @@ static int bq2415x_remove(struct i2c_client *client)
power_supply_unreg_notifier(&bq->nb); power_supply_unreg_notifier(&bq->nb);
of_node_put(bq->notify_node); of_node_put(bq->notify_node);
bq2415x_sysfs_exit(bq);
bq2415x_power_supply_exit(bq); bq2415x_power_supply_exit(bq);
bq2415x_reset_chip(bq); bq2415x_reset_chip(bq);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/extcon-provider.h>
#define BQ24190_MANUFACTURER "Texas Instruments" #define BQ24190_MANUFACTURER "Texas Instruments"
...@@ -142,7 +143,7 @@ ...@@ -142,7 +143,7 @@
#define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) #define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3))
#define BQ24190_REG_VPRS_PN_SHIFT 3 #define BQ24190_REG_VPRS_PN_SHIFT 3
#define BQ24190_REG_VPRS_PN_24190 0x4 #define BQ24190_REG_VPRS_PN_24190 0x4
#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */ #define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193, 24196 */
#define BQ24190_REG_VPRS_PN_24192I 0x3 #define BQ24190_REG_VPRS_PN_24192I 0x3
#define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2)
#define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2
...@@ -159,6 +160,7 @@ ...@@ -159,6 +160,7 @@
struct bq24190_dev_info { struct bq24190_dev_info {
struct i2c_client *client; struct i2c_client *client;
struct device *dev; struct device *dev;
struct extcon_dev *edev;
struct power_supply *charger; struct power_supply *charger;
struct power_supply *battery; struct power_supply *battery;
struct delayed_work input_current_limit_work; struct delayed_work input_current_limit_work;
...@@ -174,6 +176,11 @@ struct bq24190_dev_info { ...@@ -174,6 +176,11 @@ struct bq24190_dev_info {
u8 watchdog; u8 watchdog;
}; };
static const unsigned int bq24190_usb_extcon_cable[] = {
EXTCON_USB,
EXTCON_NONE,
};
/* /*
* The tables below provide a 2-way mapping for the value that goes in * The tables below provide a 2-way mapping for the value that goes in
* the register field and the real-world value that it represents. * the register field and the real-world value that it represents.
...@@ -402,9 +409,7 @@ static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = { ...@@ -402,9 +409,7 @@ static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = {
static struct attribute * static struct attribute *
bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1]; bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1];
static const struct attribute_group bq24190_sysfs_attr_group = { ATTRIBUTE_GROUPS(bq24190_sysfs);
.attrs = bq24190_sysfs_attrs,
};
static void bq24190_sysfs_init_attrs(void) static void bq24190_sysfs_init_attrs(void)
{ {
...@@ -491,26 +496,6 @@ static ssize_t bq24190_sysfs_store(struct device *dev, ...@@ -491,26 +496,6 @@ static ssize_t bq24190_sysfs_store(struct device *dev,
return count; return count;
} }
static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
{
bq24190_sysfs_init_attrs();
return sysfs_create_group(&bdi->charger->dev.kobj,
&bq24190_sysfs_attr_group);
}
static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi)
{
sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group);
}
#else
static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
{
return 0;
}
static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {}
#endif #endif
#ifdef CONFIG_REGULATOR #ifdef CONFIG_REGULATOR
...@@ -577,6 +562,7 @@ static const struct regulator_ops bq24190_vbus_ops = { ...@@ -577,6 +562,7 @@ static const struct regulator_ops bq24190_vbus_ops = {
static const struct regulator_desc bq24190_vbus_desc = { static const struct regulator_desc bq24190_vbus_desc = {
.name = "usb_otg_vbus", .name = "usb_otg_vbus",
.of_match = "usb-otg-vbus",
.type = REGULATOR_VOLTAGE, .type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.ops = &bq24190_vbus_ops, .ops = &bq24190_vbus_ops,
...@@ -1527,6 +1513,20 @@ static const struct power_supply_desc bq24190_battery_desc = { ...@@ -1527,6 +1513,20 @@ static const struct power_supply_desc bq24190_battery_desc = {
.property_is_writeable = bq24190_battery_property_is_writeable, .property_is_writeable = bq24190_battery_property_is_writeable,
}; };
static int bq24190_configure_usb_otg(struct bq24190_dev_info *bdi, u8 ss_reg)
{
bool otg_enabled;
int ret;
otg_enabled = !!(ss_reg & BQ24190_REG_SS_VBUS_STAT_MASK);
ret = extcon_set_state_sync(bdi->edev, EXTCON_USB, otg_enabled);
if (ret < 0)
dev_err(bdi->dev, "Can't set extcon state to %d: %d\n",
otg_enabled, ret);
return ret;
}
static void bq24190_check_status(struct bq24190_dev_info *bdi) static void bq24190_check_status(struct bq24190_dev_info *bdi)
{ {
const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK; const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK;
...@@ -1596,8 +1596,10 @@ static void bq24190_check_status(struct bq24190_dev_info *bdi) ...@@ -1596,8 +1596,10 @@ static void bq24190_check_status(struct bq24190_dev_info *bdi)
bdi->ss_reg = ss_reg; bdi->ss_reg = ss_reg;
} }
if (alert_charger || alert_battery) if (alert_charger || alert_battery) {
power_supply_changed(bdi->charger); power_supply_changed(bdi->charger);
bq24190_configure_usb_otg(bdi, ss_reg);
}
if (alert_battery && bdi->battery) if (alert_battery && bdi->battery)
power_supply_changed(bdi->battery); power_supply_changed(bdi->battery);
...@@ -1637,8 +1639,12 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi) ...@@ -1637,8 +1639,12 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (v != BQ24190_REG_VPRS_PN_24190 && switch (v) {
v != BQ24190_REG_VPRS_PN_24192I) { case BQ24190_REG_VPRS_PN_24190:
case BQ24190_REG_VPRS_PN_24192:
case BQ24190_REG_VPRS_PN_24192I:
break;
default:
dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v); dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v);
return -ENODEV; return -ENODEV;
} }
...@@ -1727,6 +1733,14 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1727,6 +1733,14 @@ static int bq24190_probe(struct i2c_client *client,
return -EINVAL; return -EINVAL;
} }
bdi->edev = devm_extcon_dev_allocate(dev, bq24190_usb_extcon_cable);
if (IS_ERR(bdi->edev))
return PTR_ERR(bdi->edev);
ret = devm_extcon_dev_register(dev, bdi->edev);
if (ret < 0)
return ret;
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 600); pm_runtime_set_autosuspend_delay(dev, 600);
...@@ -1736,6 +1750,11 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1736,6 +1750,11 @@ static int bq24190_probe(struct i2c_client *client,
goto out_pmrt; goto out_pmrt;
} }
#ifdef CONFIG_SYSFS
bq24190_sysfs_init_attrs();
charger_cfg.attr_grp = bq24190_sysfs_groups;
#endif
charger_cfg.drv_data = bdi; charger_cfg.drv_data = bdi;
charger_cfg.of_node = dev->of_node; charger_cfg.of_node = dev->of_node;
charger_cfg.supplied_to = bq24190_charger_supplied_to; charger_cfg.supplied_to = bq24190_charger_supplied_to;
...@@ -1773,11 +1792,9 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1773,11 +1792,9 @@ static int bq24190_probe(struct i2c_client *client,
goto out_charger; goto out_charger;
} }
ret = bq24190_sysfs_create_group(bdi); ret = bq24190_configure_usb_otg(bdi, bdi->ss_reg);
if (ret < 0) { if (ret < 0)
dev_err(dev, "Can't create sysfs entries\n");
goto out_charger; goto out_charger;
}
bdi->initialized = true; bdi->initialized = true;
...@@ -1787,12 +1804,12 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1787,12 +1804,12 @@ static int bq24190_probe(struct i2c_client *client,
"bq24190-charger", bdi); "bq24190-charger", bdi);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Can't set up irq handler\n"); dev_err(dev, "Can't set up irq handler\n");
goto out_sysfs; goto out_charger;
} }
ret = bq24190_register_vbus_regulator(bdi); ret = bq24190_register_vbus_regulator(bdi);
if (ret < 0) if (ret < 0)
goto out_sysfs; goto out_charger;
enable_irq_wake(client->irq); enable_irq_wake(client->irq);
...@@ -1801,9 +1818,6 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1801,9 +1818,6 @@ static int bq24190_probe(struct i2c_client *client,
return 0; return 0;
out_sysfs:
bq24190_sysfs_remove_group(bdi);
out_charger: out_charger:
if (!IS_ERR_OR_NULL(bdi->battery)) if (!IS_ERR_OR_NULL(bdi->battery))
power_supply_unregister(bdi->battery); power_supply_unregister(bdi->battery);
...@@ -1828,7 +1842,6 @@ static int bq24190_remove(struct i2c_client *client) ...@@ -1828,7 +1842,6 @@ static int bq24190_remove(struct i2c_client *client)
} }
bq24190_register_reset(bdi); bq24190_register_reset(bdi);
bq24190_sysfs_remove_group(bdi);
if (bdi->battery) if (bdi->battery)
power_supply_unregister(bdi->battery); power_supply_unregister(bdi->battery);
power_supply_unregister(bdi->charger); power_supply_unregister(bdi->charger);
...@@ -1931,7 +1944,9 @@ static const struct dev_pm_ops bq24190_pm_ops = { ...@@ -1931,7 +1944,9 @@ static const struct dev_pm_ops bq24190_pm_ops = {
static const struct i2c_device_id bq24190_i2c_ids[] = { static const struct i2c_device_id bq24190_i2c_ids[] = {
{ "bq24190" }, { "bq24190" },
{ "bq24192" },
{ "bq24192i" }, { "bq24192i" },
{ "bq24196" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
...@@ -1939,7 +1954,9 @@ MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); ...@@ -1939,7 +1954,9 @@ MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id bq24190_of_match[] = { static const struct of_device_id bq24190_of_match[] = {
{ .compatible = "ti,bq24190", }, { .compatible = "ti,bq24190", },
{ .compatible = "ti,bq24192", },
{ .compatible = "ti,bq24192i", }, { .compatible = "ti,bq24192i", },
{ .compatible = "ti,bq24196", },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, bq24190_of_match); MODULE_DEVICE_TABLE(of, bq24190_of_match);
......
...@@ -845,7 +845,7 @@ static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO, ...@@ -845,7 +845,7 @@ static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
static DEVICE_ATTR(sysoff_enable, S_IWUSR | S_IRUGO, static DEVICE_ATTR(sysoff_enable, S_IWUSR | S_IRUGO,
bq24257_sysfs_show_enable, bq24257_sysfs_set_enable); bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
static struct attribute *bq24257_charger_attr[] = { static struct attribute *bq24257_charger_sysfs_attrs[] = {
&dev_attr_ovp_voltage.attr, &dev_attr_ovp_voltage.attr,
&dev_attr_in_dpm_voltage.attr, &dev_attr_in_dpm_voltage.attr,
&dev_attr_high_impedance_enable.attr, &dev_attr_high_impedance_enable.attr,
...@@ -853,14 +853,13 @@ static struct attribute *bq24257_charger_attr[] = { ...@@ -853,14 +853,13 @@ static struct attribute *bq24257_charger_attr[] = {
NULL, NULL,
}; };
static const struct attribute_group bq24257_attr_group = { ATTRIBUTE_GROUPS(bq24257_charger_sysfs);
.attrs = bq24257_charger_attr,
};
static int bq24257_power_supply_init(struct bq24257_device *bq) static int bq24257_power_supply_init(struct bq24257_device *bq)
{ {
struct power_supply_config psy_cfg = { .drv_data = bq, }; struct power_supply_config psy_cfg = { .drv_data = bq, };
psy_cfg.attr_grp = bq24257_charger_sysfs_groups;
psy_cfg.supplied_to = bq24257_charger_supplied_to; psy_cfg.supplied_to = bq24257_charger_supplied_to;
psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to); psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
...@@ -1084,12 +1083,6 @@ static int bq24257_probe(struct i2c_client *client, ...@@ -1084,12 +1083,6 @@ static int bq24257_probe(struct i2c_client *client,
return ret; return ret;
} }
ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
if (ret < 0) {
dev_err(dev, "Can't create sysfs entries\n");
return ret;
}
return 0; return 0;
} }
...@@ -1100,8 +1093,6 @@ static int bq24257_remove(struct i2c_client *client) ...@@ -1100,8 +1093,6 @@ static int bq24257_remove(struct i2c_client *client)
if (bq->iilimit_autoset_enable) if (bq->iilimit_autoset_enable)
cancel_delayed_work_sync(&bq->iilimit_setup_work); cancel_delayed_work_sync(&bq->iilimit_setup_work);
sysfs_remove_group(&bq->charger->dev.kobj, &bq24257_attr_group);
bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */ bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
return 0; return 0;
......
...@@ -183,7 +183,7 @@ static const struct reg_field bq25890_reg_fields[] = { ...@@ -183,7 +183,7 @@ static const struct reg_field bq25890_reg_fields[] = {
[F_CHG_TMR] = REG_FIELD(0x07, 1, 2), [F_CHG_TMR] = REG_FIELD(0x07, 1, 2),
[F_JEITA_ISET] = REG_FIELD(0x07, 0, 0), [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0),
/* REG08 */ /* REG08 */
[F_BATCMP] = REG_FIELD(0x08, 6, 7), // 5-7 on BQ25896 [F_BATCMP] = REG_FIELD(0x08, 5, 7),
[F_VCLAMP] = REG_FIELD(0x08, 2, 4), [F_VCLAMP] = REG_FIELD(0x08, 2, 4),
[F_TREG] = REG_FIELD(0x08, 0, 1), [F_TREG] = REG_FIELD(0x08, 0, 1),
/* REG09 */ /* REG09 */
......
...@@ -363,7 +363,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) ...@@ -363,7 +363,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
int err = 0, i; int err = 0, i;
struct charger_desc *desc = cm->desc; struct charger_desc *desc = cm->desc;
/* Ignore if it's redundent command */ /* Ignore if it's redundant command */
if (enable == cm->charger_enabled) if (enable == cm->charger_enabled)
return 0; return 0;
...@@ -1212,7 +1212,6 @@ static int charger_extcon_init(struct charger_manager *cm, ...@@ -1212,7 +1212,6 @@ static int charger_extcon_init(struct charger_manager *cm,
if (ret < 0) { if (ret < 0) {
pr_info("Cannot register extcon_dev for %s(cable: %s)\n", pr_info("Cannot register extcon_dev for %s(cable: %s)\n",
cable->extcon_name, cable->name); cable->extcon_name, cable->name);
ret = -EINVAL;
} }
return ret; return ret;
...@@ -1352,7 +1351,7 @@ static ssize_t charger_externally_control_store(struct device *dev, ...@@ -1352,7 +1351,7 @@ static ssize_t charger_externally_control_store(struct device *dev,
} }
/** /**
* charger_manager_register_sysfs - Register sysfs entry for each charger * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger
* @cm: the Charger Manager representing the battery. * @cm: the Charger Manager representing the battery.
* *
* This function add sysfs entry for charger(regulator) to control charger from * This function add sysfs entry for charger(regulator) to control charger from
...@@ -1364,34 +1363,30 @@ static ssize_t charger_externally_control_store(struct device *dev, ...@@ -1364,34 +1363,30 @@ static ssize_t charger_externally_control_store(struct device *dev,
* externally_control, this charger isn't controlled from charger-manager and * externally_control, this charger isn't controlled from charger-manager and
* always stay off state of regulator. * always stay off state of regulator.
*/ */
static int charger_manager_register_sysfs(struct charger_manager *cm) static int charger_manager_prepare_sysfs(struct charger_manager *cm)
{ {
struct charger_desc *desc = cm->desc; struct charger_desc *desc = cm->desc;
struct charger_regulator *charger; struct charger_regulator *charger;
int chargers_externally_control = 1; int chargers_externally_control = 1;
char buf[11]; char *name;
char *str;
int ret;
int i; int i;
/* Create sysfs entry to control charger(regulator) */ /* Create sysfs entry to control charger(regulator) */
for (i = 0; i < desc->num_charger_regulators; i++) { for (i = 0; i < desc->num_charger_regulators; i++) {
charger = &desc->charger_regulators[i]; charger = &desc->charger_regulators[i];
snprintf(buf, 10, "charger.%d", i); name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i);
str = devm_kzalloc(cm->dev, if (!name)
strlen(buf) + 1, GFP_KERNEL);
if (!str)
return -ENOMEM; return -ENOMEM;
strcpy(str, buf);
charger->attrs[0] = &charger->attr_name.attr; charger->attrs[0] = &charger->attr_name.attr;
charger->attrs[1] = &charger->attr_state.attr; charger->attrs[1] = &charger->attr_state.attr;
charger->attrs[2] = &charger->attr_externally_control.attr; charger->attrs[2] = &charger->attr_externally_control.attr;
charger->attrs[3] = NULL; charger->attrs[3] = NULL;
charger->attr_g.name = str;
charger->attr_g.attrs = charger->attrs; charger->attr_grp.name = name;
charger->attr_grp.attrs = charger->attrs;
desc->sysfs_groups[i] = &charger->attr_grp;
sysfs_attr_init(&charger->attr_name.attr); sysfs_attr_init(&charger->attr_name.attr);
charger->attr_name.attr.name = "name"; charger->attr_name.attr.name = "name";
...@@ -1418,14 +1413,6 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) ...@@ -1418,14 +1413,6 @@ static int charger_manager_register_sysfs(struct charger_manager *cm)
dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
charger->regulator_name, charger->externally_control); charger->regulator_name, charger->externally_control);
ret = sysfs_create_group(&cm->charger_psy->dev.kobj,
&charger->attr_g);
if (ret < 0) {
dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n",
charger->regulator_name);
return ret;
}
} }
if (chargers_externally_control) { if (chargers_externally_control) {
...@@ -1521,19 +1508,19 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) ...@@ -1521,19 +1508,19 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev)
/* chargers */ /* chargers */
of_property_read_u32(np, "cm-num-chargers", &num_chgs); of_property_read_u32(np, "cm-num-chargers", &num_chgs);
if (num_chgs) { if (num_chgs) {
int i;
/* Allocate empty bin at the tail of array */ /* Allocate empty bin at the tail of array */
desc->psy_charger_stat = devm_kcalloc(dev, desc->psy_charger_stat = devm_kcalloc(dev,
num_chgs + 1, num_chgs + 1,
sizeof(char *), sizeof(char *),
GFP_KERNEL); GFP_KERNEL);
if (desc->psy_charger_stat) { if (!desc->psy_charger_stat)
int i; return ERR_PTR(-ENOMEM);
for (i = 0; i < num_chgs; i++) for (i = 0; i < num_chgs; i++)
of_property_read_string_index(np, "cm-chargers", of_property_read_string_index(np, "cm-chargers",
i, &desc->psy_charger_stat[i]); i, &desc->psy_charger_stat[i]);
} else {
return ERR_PTR(-ENOMEM);
}
} }
of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
...@@ -1566,6 +1553,13 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) ...@@ -1566,6 +1553,13 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev)
desc->charger_regulators = chg_regs; desc->charger_regulators = chg_regs;
desc->sysfs_groups = devm_kcalloc(dev,
desc->num_charger_regulators + 1,
sizeof(*desc->sysfs_groups),
GFP_KERNEL);
if (!desc->sysfs_groups)
return ERR_PTR(-ENOMEM);
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
struct charger_cable *cables; struct charger_cable *cables;
struct device_node *_child; struct device_node *_child;
...@@ -1633,7 +1627,7 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1633,7 +1627,7 @@ static int charger_manager_probe(struct platform_device *pdev)
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
dev_err(&pdev->dev, "No platform data (desc) found\n"); dev_err(&pdev->dev, "No platform data (desc) found\n");
return -ENODEV; return PTR_ERR(desc);
} }
cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
...@@ -1687,10 +1681,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1687,10 +1681,6 @@ static int charger_manager_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
/* Counting index only */
while (desc->psy_charger_stat[i])
i++;
/* Check if charger's supplies are present at probe */ /* Check if charger's supplies are present at probe */
for (i = 0; desc->psy_charger_stat[i]; i++) { for (i = 0; desc->psy_charger_stat[i]; i++) {
struct power_supply *psy; struct power_supply *psy;
...@@ -1772,6 +1762,15 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1772,6 +1762,15 @@ static int charger_manager_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
/* Register sysfs entry for charger(regulator) */
ret = charger_manager_prepare_sysfs(cm);
if (ret < 0) {
dev_err(&pdev->dev,
"Cannot prepare sysfs entry of regulators\n");
return ret;
}
psy_cfg.attr_grp = desc->sysfs_groups;
cm->charger_psy = power_supply_register(&pdev->dev, cm->charger_psy = power_supply_register(&pdev->dev,
&cm->charger_psy_desc, &cm->charger_psy_desc,
&psy_cfg); &psy_cfg);
...@@ -1788,14 +1787,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1788,14 +1787,6 @@ static int charger_manager_probe(struct platform_device *pdev)
goto err_reg_extcon; goto err_reg_extcon;
} }
/* Register sysfs entry for charger(regulator) */
ret = charger_manager_register_sysfs(cm);
if (ret < 0) {
dev_err(&pdev->dev,
"Cannot initialize sysfs entry of regulator\n");
goto err_reg_sysfs;
}
/* Add to the list */ /* Add to the list */
mutex_lock(&cm_list_mtx); mutex_lock(&cm_list_mtx);
list_add(&cm->entry, &cm_list); list_add(&cm->entry, &cm_list);
...@@ -1803,7 +1794,7 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1803,7 +1794,7 @@ static int charger_manager_probe(struct platform_device *pdev)
/* /*
* Charger-manager is capable of waking up the systme from sleep * Charger-manager is capable of waking up the systme from sleep
* when event is happend through cm_notify_event() * when event is happened through cm_notify_event()
*/ */
device_init_wakeup(&pdev->dev, true); device_init_wakeup(&pdev->dev, true);
device_set_wakeup_capable(&pdev->dev, false); device_set_wakeup_capable(&pdev->dev, false);
...@@ -1819,14 +1810,6 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1819,14 +1810,6 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0; return 0;
err_reg_sysfs:
for (i = 0; i < desc->num_charger_regulators; i++) {
struct charger_regulator *charger;
charger = &desc->charger_regulators[i];
sysfs_remove_group(&cm->charger_psy->dev.kobj,
&charger->attr_g);
}
err_reg_extcon: err_reg_extcon:
for (i = 0; i < desc->num_charger_regulators; i++) { for (i = 0; i < desc->num_charger_regulators; i++) {
struct charger_regulator *charger; struct charger_regulator *charger;
...@@ -2023,7 +2006,7 @@ module_exit(charger_manager_cleanup); ...@@ -2023,7 +2006,7 @@ module_exit(charger_manager_cleanup);
* cm_notify_event - charger driver notify Charger Manager of charger event * cm_notify_event - charger driver notify Charger Manager of charger event
* @psy: pointer to instance of charger's power_supply * @psy: pointer to instance of charger's power_supply
* @type: type of charger event * @type: type of charger event
* @msg: optional message passed to uevent_notify fuction * @msg: optional message passed to uevent_notify function
*/ */
void cm_notify_event(struct power_supply *psy, enum cm_event_types type, void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
char *msg) char *msg)
......
...@@ -620,7 +620,7 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, ...@@ -620,7 +620,7 @@ static int cpcap_battery_init_irq(struct platform_device *pdev,
static int cpcap_battery_init_interrupts(struct platform_device *pdev, static int cpcap_battery_init_interrupts(struct platform_device *pdev,
struct cpcap_battery_ddata *ddata) struct cpcap_battery_ddata *ddata)
{ {
const char * const cpcap_battery_irqs[] = { static const char * const cpcap_battery_irqs[] = {
"eol", "lowbph", "lowbpl", "eol", "lowbph", "lowbpl",
"chrgcurr1", "battdetb" "chrgcurr1", "battdetb"
}; };
......
...@@ -658,7 +658,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, ...@@ -658,7 +658,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
return count; return count;
} }
static const struct bin_attribute ds2780_param_eeprom_bin_attr = { static struct bin_attribute ds2780_param_eeprom_bin_attr = {
.attr = { .attr = {
.name = "param_eeprom", .name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR, .mode = S_IRUGO | S_IWUSR,
...@@ -703,7 +703,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, ...@@ -703,7 +703,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
return count; return count;
} }
static const struct bin_attribute ds2780_user_eeprom_bin_attr = { static struct bin_attribute ds2780_user_eeprom_bin_attr = {
.attr = { .attr = {
.name = "user_eeprom", .name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR, .mode = S_IRUGO | S_IWUSR,
...@@ -722,8 +722,7 @@ static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting, ...@@ -722,8 +722,7 @@ static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting,
static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin, static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin,
ds2780_set_pio_pin); ds2780_set_pio_pin);
static struct attribute *ds2780_sysfs_attrs[] = {
static struct attribute *ds2780_attributes[] = {
&dev_attr_pmod_enabled.attr, &dev_attr_pmod_enabled.attr,
&dev_attr_sense_resistor_value.attr, &dev_attr_sense_resistor_value.attr,
&dev_attr_rsgain_setting.attr, &dev_attr_rsgain_setting.attr,
...@@ -731,21 +730,30 @@ static struct attribute *ds2780_attributes[] = { ...@@ -731,21 +730,30 @@ static struct attribute *ds2780_attributes[] = {
NULL NULL
}; };
static const struct attribute_group ds2780_attr_group = { static struct bin_attribute *ds2780_sysfs_bin_attrs[] = {
.attrs = ds2780_attributes, &ds2780_param_eeprom_bin_attr,
&ds2780_user_eeprom_bin_attr,
NULL
};
static const struct attribute_group ds2780_sysfs_group = {
.attrs = ds2780_sysfs_attrs,
.bin_attrs = ds2780_sysfs_bin_attrs,
};
static const struct attribute_group *ds2780_sysfs_groups[] = {
&ds2780_sysfs_group,
NULL,
}; };
static int ds2780_battery_probe(struct platform_device *pdev) static int ds2780_battery_probe(struct platform_device *pdev)
{ {
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
int ret = 0;
struct ds2780_device_info *dev_info; struct ds2780_device_info *dev_info;
dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL); dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
if (!dev_info) { if (!dev_info)
ret = -ENOMEM; return -ENOMEM;
goto fail;
}
platform_set_drvdata(pdev, dev_info); platform_set_drvdata(pdev, dev_info);
...@@ -758,62 +766,16 @@ static int ds2780_battery_probe(struct platform_device *pdev) ...@@ -758,62 +766,16 @@ static int ds2780_battery_probe(struct platform_device *pdev)
dev_info->bat_desc.get_property = ds2780_battery_get_property; dev_info->bat_desc.get_property = ds2780_battery_get_property;
psy_cfg.drv_data = dev_info; psy_cfg.drv_data = dev_info;
psy_cfg.attr_grp = ds2780_sysfs_groups;
dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc, dev_info->bat = devm_power_supply_register(&pdev->dev,
&dev_info->bat_desc,
&psy_cfg); &psy_cfg);
if (IS_ERR(dev_info->bat)) { if (IS_ERR(dev_info->bat)) {
dev_err(dev_info->dev, "failed to register battery\n"); dev_err(dev_info->dev, "failed to register battery\n");
ret = PTR_ERR(dev_info->bat); return PTR_ERR(dev_info->bat);
goto fail;
}
ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
if (ret) {
dev_err(dev_info->dev, "failed to create sysfs group\n");
goto fail_unregister;
} }
ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
&ds2780_param_eeprom_bin_attr);
if (ret) {
dev_err(dev_info->dev,
"failed to create param eeprom bin file");
goto fail_remove_group;
}
ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
&ds2780_user_eeprom_bin_attr);
if (ret) {
dev_err(dev_info->dev,
"failed to create user eeprom bin file");
goto fail_remove_bin_file;
}
return 0;
fail_remove_bin_file:
sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
&ds2780_param_eeprom_bin_attr);
fail_remove_group:
sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
fail_unregister:
power_supply_unregister(dev_info->bat);
fail:
return ret;
}
static int ds2780_battery_remove(struct platform_device *pdev)
{
struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
/*
* Remove attributes before unregistering power supply
* because 'bat' will be freed on power_supply_unregister() call.
*/
sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
power_supply_unregister(dev_info->bat);
return 0; return 0;
} }
...@@ -822,7 +784,6 @@ static struct platform_driver ds2780_battery_driver = { ...@@ -822,7 +784,6 @@ static struct platform_driver ds2780_battery_driver = {
.name = "ds2780-battery", .name = "ds2780-battery",
}, },
.probe = ds2780_battery_probe, .probe = ds2780_battery_probe,
.remove = ds2780_battery_remove,
}; };
module_platform_driver(ds2780_battery_driver); module_platform_driver(ds2780_battery_driver);
......
...@@ -660,7 +660,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, ...@@ -660,7 +660,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
return count; return count;
} }
static const struct bin_attribute ds2781_param_eeprom_bin_attr = { static struct bin_attribute ds2781_param_eeprom_bin_attr = {
.attr = { .attr = {
.name = "param_eeprom", .name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR, .mode = S_IRUGO | S_IWUSR,
...@@ -706,7 +706,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, ...@@ -706,7 +706,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
return count; return count;
} }
static const struct bin_attribute ds2781_user_eeprom_bin_attr = { static struct bin_attribute ds2781_user_eeprom_bin_attr = {
.attr = { .attr = {
.name = "user_eeprom", .name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR, .mode = S_IRUGO | S_IWUSR,
...@@ -725,8 +725,7 @@ static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting, ...@@ -725,8 +725,7 @@ static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting,
static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin, static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin,
ds2781_set_pio_pin); ds2781_set_pio_pin);
static struct attribute *ds2781_sysfs_attrs[] = {
static struct attribute *ds2781_attributes[] = {
&dev_attr_pmod_enabled.attr, &dev_attr_pmod_enabled.attr,
&dev_attr_sense_resistor_value.attr, &dev_attr_sense_resistor_value.attr,
&dev_attr_rsgain_setting.attr, &dev_attr_rsgain_setting.attr,
...@@ -734,14 +733,26 @@ static struct attribute *ds2781_attributes[] = { ...@@ -734,14 +733,26 @@ static struct attribute *ds2781_attributes[] = {
NULL NULL
}; };
static const struct attribute_group ds2781_attr_group = { static struct bin_attribute *ds2781_sysfs_bin_attrs[] = {
.attrs = ds2781_attributes, &ds2781_param_eeprom_bin_attr,
&ds2781_user_eeprom_bin_attr,
NULL,
};
static const struct attribute_group ds2781_sysfs_group = {
.attrs = ds2781_sysfs_attrs,
.bin_attrs = ds2781_sysfs_bin_attrs,
};
static const struct attribute_group *ds2781_sysfs_groups[] = {
&ds2781_sysfs_group,
NULL,
}; };
static int ds2781_battery_probe(struct platform_device *pdev) static int ds2781_battery_probe(struct platform_device *pdev)
{ {
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
int ret = 0;
struct ds2781_device_info *dev_info; struct ds2781_device_info *dev_info;
dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL); dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
...@@ -759,62 +770,16 @@ static int ds2781_battery_probe(struct platform_device *pdev) ...@@ -759,62 +770,16 @@ static int ds2781_battery_probe(struct platform_device *pdev)
dev_info->bat_desc.get_property = ds2781_battery_get_property; dev_info->bat_desc.get_property = ds2781_battery_get_property;
psy_cfg.drv_data = dev_info; psy_cfg.drv_data = dev_info;
psy_cfg.attr_grp = ds2781_sysfs_groups;
dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc, dev_info->bat = devm_power_supply_register(&pdev->dev,
&dev_info->bat_desc,
&psy_cfg); &psy_cfg);
if (IS_ERR(dev_info->bat)) { if (IS_ERR(dev_info->bat)) {
dev_err(dev_info->dev, "failed to register battery\n"); dev_err(dev_info->dev, "failed to register battery\n");
ret = PTR_ERR(dev_info->bat); return PTR_ERR(dev_info->bat);
goto fail;
}
ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
if (ret) {
dev_err(dev_info->dev, "failed to create sysfs group\n");
goto fail_unregister;
}
ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
&ds2781_param_eeprom_bin_attr);
if (ret) {
dev_err(dev_info->dev,
"failed to create param eeprom bin file");
goto fail_remove_group;
}
ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
&ds2781_user_eeprom_bin_attr);
if (ret) {
dev_err(dev_info->dev,
"failed to create user eeprom bin file");
goto fail_remove_bin_file;
} }
return 0;
fail_remove_bin_file:
sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
&ds2781_param_eeprom_bin_attr);
fail_remove_group:
sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
fail_unregister:
power_supply_unregister(dev_info->bat);
fail:
return ret;
}
static int ds2781_battery_remove(struct platform_device *pdev)
{
struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
/*
* Remove attributes before unregistering power supply
* because 'bat' will be freed on power_supply_unregister() call.
*/
sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
power_supply_unregister(dev_info->bat);
return 0; return 0;
} }
...@@ -823,7 +788,6 @@ static struct platform_driver ds2781_battery_driver = { ...@@ -823,7 +788,6 @@ static struct platform_driver ds2781_battery_driver = {
.name = "ds2781-battery", .name = "ds2781-battery",
}, },
.probe = ds2781_battery_probe, .probe = ds2781_battery_probe,
.remove = ds2781_battery_remove,
}; };
module_platform_driver(ds2781_battery_driver); module_platform_driver(ds2781_battery_driver);
......
...@@ -82,11 +82,11 @@ static enum power_supply_type gpio_charger_get_type(struct device *dev) ...@@ -82,11 +82,11 @@ static enum power_supply_type gpio_charger_get_type(struct device *dev)
if (!strcmp("usb-sdp", chargetype)) if (!strcmp("usb-sdp", chargetype))
return POWER_SUPPLY_TYPE_USB; return POWER_SUPPLY_TYPE_USB;
if (!strcmp("usb-dcp", chargetype)) if (!strcmp("usb-dcp", chargetype))
return POWER_SUPPLY_TYPE_USB_DCP; return POWER_SUPPLY_TYPE_USB;
if (!strcmp("usb-cdp", chargetype)) if (!strcmp("usb-cdp", chargetype))
return POWER_SUPPLY_TYPE_USB_CDP; return POWER_SUPPLY_TYPE_USB;
if (!strcmp("usb-aca", chargetype)) if (!strcmp("usb-aca", chargetype))
return POWER_SUPPLY_TYPE_USB_ACA; return POWER_SUPPLY_TYPE_USB;
} }
dev_warn(dev, "unknown charger type %s\n", chargetype); dev_warn(dev, "unknown charger type %s\n", chargetype);
......
...@@ -410,30 +410,6 @@ static const struct power_supply_desc lp8788_psy_battery_desc = { ...@@ -410,30 +410,6 @@ static const struct power_supply_desc lp8788_psy_battery_desc = {
.get_property = lp8788_battery_get_property, .get_property = lp8788_battery_get_property,
}; };
static int lp8788_psy_register(struct platform_device *pdev,
struct lp8788_charger *pchg)
{
struct power_supply_config charger_cfg = {};
charger_cfg.supplied_to = battery_supplied_to;
charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
pchg->charger = power_supply_register(&pdev->dev,
&lp8788_psy_charger_desc,
&charger_cfg);
if (IS_ERR(pchg->charger))
return -EPERM;
pchg->battery = power_supply_register(&pdev->dev,
&lp8788_psy_battery_desc, NULL);
if (IS_ERR(pchg->battery)) {
power_supply_unregister(pchg->charger);
return -EPERM;
}
return 0;
}
static void lp8788_psy_unregister(struct lp8788_charger *pchg) static void lp8788_psy_unregister(struct lp8788_charger *pchg)
{ {
power_supply_unregister(pchg->battery); power_supply_unregister(pchg->battery);
...@@ -690,16 +666,39 @@ static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL); ...@@ -690,16 +666,39 @@ static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);
static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL); static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL);
static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL); static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL);
static struct attribute *lp8788_charger_attr[] = { static struct attribute *lp8788_charger_sysfs_attrs[] = {
&dev_attr_charger_status.attr, &dev_attr_charger_status.attr,
&dev_attr_eoc_time.attr, &dev_attr_eoc_time.attr,
&dev_attr_eoc_level.attr, &dev_attr_eoc_level.attr,
NULL, NULL,
}; };
static const struct attribute_group lp8788_attr_group = { ATTRIBUTE_GROUPS(lp8788_charger_sysfs);
.attrs = lp8788_charger_attr,
}; static int lp8788_psy_register(struct platform_device *pdev,
struct lp8788_charger *pchg)
{
struct power_supply_config charger_cfg = {};
charger_cfg.attr_grp = lp8788_charger_sysfs_groups;
charger_cfg.supplied_to = battery_supplied_to;
charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
pchg->charger = power_supply_register(&pdev->dev,
&lp8788_psy_charger_desc,
&charger_cfg);
if (IS_ERR(pchg->charger))
return -EPERM;
pchg->battery = power_supply_register(&pdev->dev,
&lp8788_psy_battery_desc, NULL);
if (IS_ERR(pchg->battery)) {
power_supply_unregister(pchg->charger);
return -EPERM;
}
return 0;
}
static int lp8788_charger_probe(struct platform_device *pdev) static int lp8788_charger_probe(struct platform_device *pdev)
{ {
...@@ -726,12 +725,6 @@ static int lp8788_charger_probe(struct platform_device *pdev) ...@@ -726,12 +725,6 @@ static int lp8788_charger_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
if (ret) {
lp8788_psy_unregister(pchg);
return ret;
}
ret = lp8788_irq_register(pdev, pchg); ret = lp8788_irq_register(pdev, pchg);
if (ret) if (ret)
dev_warn(dev, "failed to register charger irq: %d\n", ret); dev_warn(dev, "failed to register charger irq: %d\n", ret);
...@@ -745,7 +738,6 @@ static int lp8788_charger_remove(struct platform_device *pdev) ...@@ -745,7 +738,6 @@ static int lp8788_charger_remove(struct platform_device *pdev)
flush_work(&pchg->charger_work); flush_work(&pchg->charger_work);
lp8788_irq_unregister(pdev, pchg); lp8788_irq_unregister(pdev, pchg);
sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
lp8788_psy_unregister(pchg); lp8788_psy_unregister(pchg);
lp8788_release_adc_channel(pchg); lp8788_release_adc_channel(pchg);
......
...@@ -428,14 +428,14 @@ static int olpc_bat_get_property(struct power_supply *psy, ...@@ -428,14 +428,14 @@ static int olpc_bat_get_property(struct power_supply *psy,
if (ret) if (ret)
return ret; return ret;
val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256; val->intval = (s16)be16_to_cpu(ec_word) * 10 / 256;
break; break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT: case POWER_SUPPLY_PROP_TEMP_AMBIENT:
ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
if (ret) if (ret)
return ret; return ret;
val->intval = (int)be16_to_cpu(ec_word) * 100 / 256; val->intval = (int)be16_to_cpu(ec_word) * 10 / 256;
break; break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER: case POWER_SUPPLY_PROP_CHARGE_COUNTER:
ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2); ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
......
...@@ -245,17 +245,14 @@ static ssize_t set_chglim(struct device *dev, ...@@ -245,17 +245,14 @@ static ssize_t set_chglim(struct device *dev,
*/ */
static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim); static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
static struct attribute *pcf50633_mbc_sysfs_entries[] = { static struct attribute *pcf50633_mbc_sysfs_attrs[] = {
&dev_attr_chgmode.attr, &dev_attr_chgmode.attr,
&dev_attr_usb_curlim.attr, &dev_attr_usb_curlim.attr,
&dev_attr_chg_curlim.attr, &dev_attr_chg_curlim.attr,
NULL, NULL,
}; };
static const struct attribute_group mbc_attr_group = { ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
.name = NULL, /* put in device directory */
.attrs = pcf50633_mbc_sysfs_entries,
};
static void static void
pcf50633_mbc_irq_handler(int irq, void *data) pcf50633_mbc_irq_handler(int irq, void *data)
...@@ -390,6 +387,7 @@ static const struct power_supply_desc pcf50633_mbc_ac_desc = { ...@@ -390,6 +387,7 @@ static const struct power_supply_desc pcf50633_mbc_ac_desc = {
static int pcf50633_mbc_probe(struct platform_device *pdev) static int pcf50633_mbc_probe(struct platform_device *pdev)
{ {
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct power_supply_config usb_psy_cfg;
struct pcf50633_mbc *mbc; struct pcf50633_mbc *mbc;
int i; int i;
u8 mbcs1; u8 mbcs1;
...@@ -419,8 +417,11 @@ static int pcf50633_mbc_probe(struct platform_device *pdev) ...@@ -419,8 +417,11 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
return PTR_ERR(mbc->adapter); return PTR_ERR(mbc->adapter);
} }
usb_psy_cfg = psy_cfg;
usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups;
mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc, mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
&psy_cfg); &usb_psy_cfg);
if (IS_ERR(mbc->usb)) { if (IS_ERR(mbc->usb)) {
dev_err(mbc->pcf->dev, "failed to register usb\n"); dev_err(mbc->pcf->dev, "failed to register usb\n");
power_supply_unregister(mbc->adapter); power_supply_unregister(mbc->adapter);
...@@ -436,9 +437,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev) ...@@ -436,9 +437,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
return PTR_ERR(mbc->ac); return PTR_ERR(mbc->ac);
} }
if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))
dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1); mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
if (mbcs1 & PCF50633_MBCS1_USBPRES) if (mbcs1 & PCF50633_MBCS1_USBPRES)
pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc); pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
...@@ -457,7 +455,6 @@ static int pcf50633_mbc_remove(struct platform_device *pdev) ...@@ -457,7 +455,6 @@ static int pcf50633_mbc_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
power_supply_unregister(mbc->usb); power_supply_unregister(mbc->usb);
power_supply_unregister(mbc->adapter); power_supply_unregister(mbc->adapter);
power_supply_unregister(mbc->ac); power_supply_unregister(mbc->ac);
......
...@@ -570,7 +570,7 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -570,7 +570,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
{ {
struct device_node *battery_np; struct device_node *battery_np;
const char *value; const char *value;
int err; int err, len, index;
info->energy_full_design_uwh = -EINVAL; info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL; info->charge_full_design_uah = -EINVAL;
...@@ -579,6 +579,13 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -579,6 +579,13 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->charge_term_current_ua = -EINVAL; info->charge_term_current_ua = -EINVAL;
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;
for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
info->ocv_table[index] = NULL;
info->ocv_temp[index] = -EINVAL;
info->ocv_table_size[index] = -EINVAL;
}
if (!psy->of_node) { if (!psy->of_node) {
dev_warn(&psy->dev, "%s currently only supports devicetree\n", dev_warn(&psy->dev, "%s currently only supports devicetree\n",
...@@ -616,11 +623,142 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -616,11 +623,142 @@ int power_supply_get_battery_info(struct power_supply *psy,
&info->constant_charge_current_max_ua); &info->constant_charge_current_max_ua);
of_property_read_u32(battery_np, "constant_charge_voltage_max_microvolt", of_property_read_u32(battery_np, "constant_charge_voltage_max_microvolt",
&info->constant_charge_voltage_max_uv); &info->constant_charge_voltage_max_uv);
of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms",
&info->factory_internal_resistance_uohm);
len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius");
if (len < 0 && len != -EINVAL) {
return len;
} else if (len > POWER_SUPPLY_OCV_TEMP_MAX) {
dev_err(&psy->dev, "Too many temperature values\n");
return -EINVAL;
} else if (len > 0) {
of_property_read_u32_array(battery_np, "ocv-capacity-celsius",
info->ocv_temp, len);
}
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);
list = of_get_property(battery_np, propname, &size);
if (!list || !size) {
dev_err(&psy->dev, "failed to get %s\n", propname);
kfree(propname);
power_supply_put_battery_info(psy, info);
return -EINVAL;
}
kfree(propname);
tab_len = size / (2 * sizeof(__be32));
info->ocv_table_size[index] = tab_len;
table = info->ocv_table[index] =
devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL);
if (!info->ocv_table[index]) {
power_supply_put_battery_info(psy, info);
return -ENOMEM;
}
for (i = 0; i < tab_len; i++) {
table[i].ocv = be32_to_cpu(*list++);
table[i].capacity = be32_to_cpu(*list++);
}
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(power_supply_get_battery_info); EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
void power_supply_put_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info)
{
int i;
for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
if (info->ocv_table[i])
devm_kfree(&psy->dev, info->ocv_table[i]);
}
}
EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
/**
* power_supply_ocv2cap_simple() - find the battery capacity
* @table: Pointer to battery OCV lookup table
* @table_len: OCV table length
* @ocv: Current OCV value
*
* This helper function is used to look up battery capacity according to
* current OCV value from one OCV table, and the OCV table must be ordered
* descending.
*
* Return: the battery capacity.
*/
int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
int table_len, int ocv)
{
int i, cap, tmp;
for (i = 0; i < table_len; i++)
if (ocv > table[i].ocv)
break;
if (i > 0 && i < table_len) {
tmp = (table[i - 1].capacity - table[i].capacity) *
(ocv - table[i].ocv);
tmp /= table[i - 1].ocv - table[i].ocv;
cap = tmp + table[i].capacity;
} else if (i == 0) {
cap = table[0].capacity;
} else {
cap = table[table_len - 1].capacity;
}
return cap;
}
EXPORT_SYMBOL_GPL(power_supply_ocv2cap_simple);
struct power_supply_battery_ocv_table *
power_supply_find_ocv2cap_table(struct power_supply_battery_info *info,
int temp, int *table_len)
{
int best_temp_diff = INT_MAX, temp_diff;
u8 i, best_index = 0;
if (!info->ocv_table[0])
return NULL;
for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
temp_diff = abs(info->ocv_temp[i] - temp);
if (temp_diff < best_temp_diff) {
best_temp_diff = temp_diff;
best_index = i;
}
}
*table_len = info->ocv_table_size[best_index];
return info->ocv_table[best_index];
}
EXPORT_SYMBOL_GPL(power_supply_find_ocv2cap_table);
int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
int ocv, int temp)
{
struct power_supply_battery_ocv_table *table;
int table_len;
table = power_supply_find_ocv2cap_table(info, temp, &table_len);
if (!table)
return -EINVAL;
return power_supply_ocv2cap_simple(table, table_len, ocv);
}
EXPORT_SYMBOL_GPL(power_supply_batinfo_ocv2cap);
int power_supply_get_property(struct power_supply *psy, int power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp, enum power_supply_property psp,
union power_supply_propval *val) union power_supply_propval *val)
...@@ -880,6 +1018,7 @@ __power_supply_register(struct device *parent, ...@@ -880,6 +1018,7 @@ __power_supply_register(struct device *parent,
dev_set_drvdata(dev, psy); dev_set_drvdata(dev, psy);
psy->desc = desc; psy->desc = desc;
if (cfg) { if (cfg) {
dev->groups = cfg->attr_grp;
psy->drv_data = cfg->drv_data; psy->drv_data = cfg->drv_data;
psy->of_node = psy->of_node =
cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node; cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node;
......
...@@ -57,9 +57,11 @@ struct sc2731_charger_info { ...@@ -57,9 +57,11 @@ struct sc2731_charger_info {
struct usb_phy *usb_phy; struct usb_phy *usb_phy;
struct notifier_block usb_notify; struct notifier_block usb_notify;
struct power_supply *psy_usb; struct power_supply *psy_usb;
struct work_struct work;
struct mutex lock; struct mutex lock;
bool charging; bool charging;
u32 base; u32 base;
u32 limit;
}; };
static void sc2731_charger_stop_charge(struct sc2731_charger_info *info) static void sc2731_charger_stop_charge(struct sc2731_charger_info *info)
...@@ -318,22 +320,21 @@ static const struct power_supply_desc sc2731_charger_desc = { ...@@ -318,22 +320,21 @@ static const struct power_supply_desc sc2731_charger_desc = {
.property_is_writeable = sc2731_charger_property_is_writeable, .property_is_writeable = sc2731_charger_property_is_writeable,
}; };
static int sc2731_charger_usb_change(struct notifier_block *nb, static void sc2731_charger_work(struct work_struct *data)
unsigned long limit, void *data)
{ {
struct sc2731_charger_info *info = struct sc2731_charger_info *info =
container_of(nb, struct sc2731_charger_info, usb_notify); container_of(data, struct sc2731_charger_info, work);
int ret = 0; int ret;
mutex_lock(&info->lock); mutex_lock(&info->lock);
if (limit > 0) { if (info->limit > 0 && !info->charging) {
/* set current limitation and start to charge */ /* set current limitation and start to charge */
ret = sc2731_charger_set_current_limit(info, limit); ret = sc2731_charger_set_current_limit(info, info->limit);
if (ret) if (ret)
goto out; goto out;
ret = sc2731_charger_set_current(info, limit); ret = sc2731_charger_set_current(info, info->limit);
if (ret) if (ret)
goto out; goto out;
...@@ -342,7 +343,7 @@ static int sc2731_charger_usb_change(struct notifier_block *nb, ...@@ -342,7 +343,7 @@ static int sc2731_charger_usb_change(struct notifier_block *nb,
goto out; goto out;
info->charging = true; info->charging = true;
} else { } else if (!info->limit && info->charging) {
/* Stop charging */ /* Stop charging */
info->charging = false; info->charging = false;
sc2731_charger_stop_charge(info); sc2731_charger_stop_charge(info);
...@@ -350,7 +351,19 @@ static int sc2731_charger_usb_change(struct notifier_block *nb, ...@@ -350,7 +351,19 @@ static int sc2731_charger_usb_change(struct notifier_block *nb,
out: out:
mutex_unlock(&info->lock); mutex_unlock(&info->lock);
return ret; }
static int sc2731_charger_usb_change(struct notifier_block *nb,
unsigned long limit, void *data)
{
struct sc2731_charger_info *info =
container_of(nb, struct sc2731_charger_info, usb_notify);
info->limit = limit;
schedule_work(&info->work);
return NOTIFY_OK;
} }
static int sc2731_charger_hw_init(struct sc2731_charger_info *info) static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
...@@ -395,6 +408,8 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info) ...@@ -395,6 +408,8 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
vol_val = (term_voltage - 4200) / 100; vol_val = (term_voltage - 4200) / 100;
else else
vol_val = 0; vol_val = 0;
power_supply_put_battery_info(info->psy_usb, &bat_info);
} }
/* Set charge termination current */ /* Set charge termination current */
...@@ -419,6 +434,24 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info) ...@@ -419,6 +434,24 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
return ret; return ret;
} }
static void sc2731_charger_detect_status(struct sc2731_charger_info *info)
{
unsigned int min, max;
/*
* If the USB charger status has been USB_CHARGER_PRESENT before
* registering the notifier, we should start to charge with getting
* the charge current.
*/
if (info->usb_phy->chg_state != USB_CHARGER_PRESENT)
return;
usb_phy_get_charger_current(info->usb_phy, &min, &max);
info->limit = min;
schedule_work(&info->work);
}
static int sc2731_charger_probe(struct platform_device *pdev) static int sc2731_charger_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
...@@ -432,6 +465,7 @@ static int sc2731_charger_probe(struct platform_device *pdev) ...@@ -432,6 +465,7 @@ static int sc2731_charger_probe(struct platform_device *pdev)
mutex_init(&info->lock); mutex_init(&info->lock);
info->dev = &pdev->dev; info->dev = &pdev->dev;
INIT_WORK(&info->work, sc2731_charger_work);
info->regmap = dev_get_regmap(pdev->dev.parent, NULL); info->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!info->regmap) { if (!info->regmap) {
...@@ -472,6 +506,8 @@ static int sc2731_charger_probe(struct platform_device *pdev) ...@@ -472,6 +506,8 @@ static int sc2731_charger_probe(struct platform_device *pdev)
return ret; return ret;
} }
sc2731_charger_detect_status(info);
return 0; return 0;
} }
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Spreadtrum Communications Inc.
#include <linux/gpio/consumer.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* PMIC global control registers definition */
#define SC27XX_MODULE_EN0 0xc08
#define SC27XX_CLK_EN0 0xc18
#define SC27XX_FGU_EN BIT(7)
#define SC27XX_FGU_RTC_EN BIT(6)
/* FGU registers definition */
#define SC27XX_FGU_START 0x0
#define SC27XX_FGU_CONFIG 0x4
#define SC27XX_FGU_ADC_CONFIG 0x8
#define SC27XX_FGU_STATUS 0xc
#define SC27XX_FGU_INT_EN 0x10
#define SC27XX_FGU_INT_CLR 0x14
#define SC27XX_FGU_INT_STS 0x1c
#define SC27XX_FGU_VOLTAGE 0x20
#define SC27XX_FGU_OCV 0x24
#define SC27XX_FGU_POCV 0x28
#define SC27XX_FGU_CURRENT 0x2c
#define SC27XX_FGU_LOW_OVERLOAD 0x34
#define SC27XX_FGU_CLBCNT_SETH 0x50
#define SC27XX_FGU_CLBCNT_SETL 0x54
#define SC27XX_FGU_CLBCNT_DELTH 0x58
#define SC27XX_FGU_CLBCNT_DELTL 0x5c
#define SC27XX_FGU_CLBCNT_VALH 0x68
#define SC27XX_FGU_CLBCNT_VALL 0x6c
#define SC27XX_FGU_CLBCNT_QMAXL 0x74
#define SC27XX_FGU_USER_AREA_SET 0xa0
#define SC27XX_FGU_USER_AREA_CLEAR 0xa4
#define SC27XX_FGU_USER_AREA_STATUS 0xa8
#define SC27XX_WRITE_SELCLB_EN BIT(0)
#define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0)
#define SC27XX_FGU_CLBCNT_SHIFT 16
#define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0)
#define SC27XX_FGU_INT_MASK GENMASK(9, 0)
#define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0)
#define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2)
#define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12)
#define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0)
#define SC27XX_FGU_MODE_AREA_SHIFT 12
#define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0)
#define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0)
#define SC27XX_FGU_NORMAIL_POWERTON 0x5
#define SC27XX_FGU_CUR_BASIC_ADC 8192
#define SC27XX_FGU_SAMPLE_HZ 2
/*
* struct sc27xx_fgu_data: describe the FGU device
* @regmap: regmap for register access
* @dev: platform device
* @battery: battery power supply
* @base: the base offset for the controller
* @lock: protect the structure
* @gpiod: GPIO for battery detection
* @channel: IIO channel to get battery temperature
* @internal_resist: the battery internal resistance in mOhm
* @total_cap: the total capacity of the battery in mAh
* @init_cap: the initial capacity of the battery in mAh
* @alarm_cap: the alarm capacity
* @init_clbcnt: the initial coulomb counter
* @max_volt: the maximum constant input voltage in millivolt
* @min_volt: the minimum drained battery voltage in microvolt
* @table_len: the capacity table length
* @cur_1000ma_adc: ADC value corresponding to 1000 mA
* @vol_1000mv_adc: ADC value corresponding to 1000 mV
* @cap_table: capacity table with corresponding ocv
*/
struct sc27xx_fgu_data {
struct regmap *regmap;
struct device *dev;
struct power_supply *battery;
u32 base;
struct mutex lock;
struct gpio_desc *gpiod;
struct iio_channel *channel;
bool bat_present;
int internal_resist;
int total_cap;
int init_cap;
int alarm_cap;
int init_clbcnt;
int max_volt;
int min_volt;
int table_len;
int cur_1000ma_adc;
int vol_1000mv_adc;
struct power_supply_battery_ocv_table *cap_table;
};
static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
static const char * const sc27xx_charger_supply_name[] = {
"sc2731_charger",
"sc2720_charger",
"sc2721_charger",
"sc2723_charger",
};
static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
}
static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
}
static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
{
return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
}
static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
{
int ret, status, cap, mode;
ret = regmap_read(data->regmap,
data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
if (ret)
return false;
/*
* We use low 4 bits to save the last battery capacity and high 12 bits
* to save the system boot mode.
*/
mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
cap = status & SC27XX_FGU_CAP_AREA_MASK;
/*
* When FGU has been powered down, the user area registers became
* default value (0xffff), which can be used to valid if the system is
* first power on or not.
*/
if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
return true;
return false;
}
static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
int boot_mode)
{
int ret;
ret = regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_USER_AREA_CLEAR,
SC27XX_FGU_MODE_AREA_MASK,
SC27XX_FGU_MODE_AREA_MASK);
if (ret)
return ret;
return regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_USER_AREA_SET,
SC27XX_FGU_MODE_AREA_MASK,
boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
}
static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
{
int ret;
ret = regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_USER_AREA_CLEAR,
SC27XX_FGU_CAP_AREA_MASK,
SC27XX_FGU_CAP_AREA_MASK);
if (ret)
return ret;
return regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_USER_AREA_SET,
SC27XX_FGU_CAP_AREA_MASK, cap);
}
static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
{
int ret, value;
ret = regmap_read(data->regmap,
data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
if (ret)
return ret;
*cap = value & SC27XX_FGU_CAP_AREA_MASK;
return 0;
}
/*
* When system boots on, we can not read battery capacity from coulomb
* registers, since now the coulomb registers are invalid. So we should
* calculate the battery open circuit voltage, and get current battery
* capacity according to the capacity table.
*/
static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
{
int volt, cur, oci, ocv, ret;
bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
/*
* If system is not the first power on, we should use the last saved
* battery capacity as the initial battery capacity. Otherwise we should
* re-calculate the initial battery capacity.
*/
if (!is_first_poweron) {
ret = sc27xx_fgu_read_last_cap(data, cap);
if (ret)
return ret;
return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
}
/*
* After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
* the first sampled open circuit current.
*/
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
&cur);
if (ret)
return ret;
cur <<= 1;
oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
/*
* Should get the OCV from SC27XX_FGU_POCV register at the system
* beginning. It is ADC values reading from registers which need to
* convert the corresponding voltage.
*/
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
if (ret)
return ret;
volt = sc27xx_fgu_adc_to_voltage(data, volt);
ocv = volt * 1000 - oci * data->internal_resist;
/*
* Parse the capacity table to look up the correct capacity percent
* according to current battery's corresponding OCV values.
*/
*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
ocv);
ret = sc27xx_fgu_save_last_cap(data, *cap);
if (ret)
return ret;
return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
}
static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
{
int ret;
clbcnt *= SC27XX_FGU_SAMPLE_HZ;
ret = regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_CLBCNT_SETL,
SC27XX_FGU_CLBCNT_MASK, clbcnt);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_CLBCNT_SETH,
SC27XX_FGU_CLBCNT_MASK,
clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
if (ret)
return ret;
return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
SC27XX_WRITE_SELCLB_EN,
SC27XX_WRITE_SELCLB_EN);
}
static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
{
int ccl, cch, ret;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
&ccl);
if (ret)
return ret;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
&cch);
if (ret)
return ret;
*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
return 0;
}
static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
{
int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
/* Get current coulomb counters firstly */
ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
if (ret)
return ret;
delta_clbcnt = cur_clbcnt - data->init_clbcnt;
/*
* Convert coulomb counter to delta capacity (mAh), and set multiplier
* as 100 to improve the precision.
*/
temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
temp = sc27xx_fgu_adc_to_current(data, temp);
/*
* Convert to capacity percent of the battery total capacity,
* and multiplier is 100 too.
*/
delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
*cap = delta_cap + data->init_cap;
return 0;
}
static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
{
int ret, vol;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
if (ret)
return ret;
/*
* It is ADC values reading from registers which need to convert to
* corresponding voltage values.
*/
*val = sc27xx_fgu_adc_to_voltage(data, vol);
return 0;
}
static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
{
int ret, cur;
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
if (ret)
return ret;
/*
* It is ADC values reading from registers which need to convert to
* corresponding current values.
*/
*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
return 0;
}
static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
{
int vol, cur, ret;
ret = sc27xx_fgu_get_vbat_vol(data, &vol);
if (ret)
return ret;
ret = sc27xx_fgu_get_current(data, &cur);
if (ret)
return ret;
/* Return the battery OCV in micro volts. */
*val = vol * 1000 - cur * data->internal_resist;
return 0;
}
static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
{
return iio_read_channel_processed(data->channel, temp);
}
static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
{
int ret, vol;
ret = sc27xx_fgu_get_vbat_vol(data, &vol);
if (ret)
return ret;
if (vol > data->max_volt)
*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
else
*health = POWER_SUPPLY_HEALTH_GOOD;
return 0;
}
static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
{
union power_supply_propval val;
struct power_supply *psy;
int i, ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
if (!psy)
continue;
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
&val);
power_supply_put(psy);
if (ret)
return ret;
*status = val.intval;
}
return ret;
}
static int sc27xx_fgu_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
int ret = 0;
int value;
mutex_lock(&data->lock);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = sc27xx_fgu_get_status(data, &value);
if (ret)
goto error;
val->intval = value;
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = sc27xx_fgu_get_health(data, &value);
if (ret)
goto error;
val->intval = value;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = data->bat_present;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = sc27xx_fgu_get_temp(data, &value);
if (ret)
goto error;
val->intval = value;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = sc27xx_fgu_get_capacity(data, &value);
if (ret)
goto error;
val->intval = value;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = sc27xx_fgu_get_vbat_vol(data, &value);
if (ret)
goto error;
val->intval = value * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = sc27xx_fgu_get_vbat_ocv(data, &value);
if (ret)
goto error;
val->intval = value;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = sc27xx_fgu_get_current(data, &value);
if (ret)
goto error;
val->intval = value * 1000;
break;
default:
ret = -EINVAL;
break;
}
error:
mutex_unlock(&data->lock);
return ret;
}
static int sc27xx_fgu_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
int ret;
if (psp != POWER_SUPPLY_PROP_CAPACITY)
return -EINVAL;
mutex_lock(&data->lock);
ret = sc27xx_fgu_save_last_cap(data, val->intval);
mutex_unlock(&data->lock);
if (ret < 0)
dev_err(data->dev, "failed to save battery capacity\n");
return ret;
}
static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
{
struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
power_supply_changed(data->battery);
}
static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
return psp == POWER_SUPPLY_PROP_CAPACITY;
}
static enum power_supply_property sc27xx_fgu_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
};
static const struct power_supply_desc sc27xx_fgu_desc = {
.name = "sc27xx-fgu",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = sc27xx_fgu_props,
.num_properties = ARRAY_SIZE(sc27xx_fgu_props),
.get_property = sc27xx_fgu_get_property,
.set_property = sc27xx_fgu_set_property,
.external_power_changed = sc27xx_fgu_external_power_changed,
.property_is_writeable = sc27xx_fgu_property_is_writeable,
};
static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
{
data->init_cap = cap;
data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
}
static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
{
struct sc27xx_fgu_data *data = dev_id;
int ret, cap, ocv, adc;
u32 status;
mutex_lock(&data->lock);
ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
&status);
if (ret)
goto out;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
status, status);
if (ret)
goto out;
/*
* When low overload voltage interrupt happens, we should calibrate the
* battery capacity in lower voltage stage.
*/
if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
goto out;
ret = sc27xx_fgu_get_capacity(data, &cap);
if (ret)
goto out;
ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
if (ret)
goto out;
/*
* If current OCV value is less than the minimum OCV value in OCV table,
* which means now battery capacity is 0%, and we should adjust the
* inititial capacity to 0.
*/
if (ocv <= data->cap_table[data->table_len - 1].ocv) {
sc27xx_fgu_adjust_cap(data, 0);
} else if (ocv <= data->min_volt) {
/*
* If current OCV value is less than the low alarm voltage, but
* current capacity is larger than the alarm capacity, we should
* adjust the inititial capacity to alarm capacity.
*/
if (cap > data->alarm_cap) {
sc27xx_fgu_adjust_cap(data, data->alarm_cap);
} else if (cap <= 0) {
int cur_cap;
/*
* If current capacity is equal with 0 or less than 0
* (some error occurs), we should adjust inititial
* capacity to the capacity corresponding to current OCV
* value.
*/
cur_cap = power_supply_ocv2cap_simple(data->cap_table,
data->table_len,
ocv);
sc27xx_fgu_adjust_cap(data, cur_cap);
}
/*
* After adjusting the battery capacity, we should set the
* lowest alarm voltage instead.
*/
data->min_volt = data->cap_table[data->table_len - 1].ocv;
adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
}
out:
mutex_unlock(&data->lock);
power_supply_changed(data->battery);
return IRQ_HANDLED;
}
static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
{
struct sc27xx_fgu_data *data = dev_id;
int state;
mutex_lock(&data->lock);
state = gpiod_get_value_cansleep(data->gpiod);
if (state < 0) {
dev_err(data->dev, "failed to get gpio state\n");
mutex_unlock(&data->lock);
return IRQ_RETVAL(state);
}
data->bat_present = !!state;
mutex_unlock(&data->lock);
power_supply_changed(data->battery);
return IRQ_HANDLED;
}
static void sc27xx_fgu_disable(void *_data)
{
struct sc27xx_fgu_data *data = _data;
regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
}
static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
{
/*
* Get current capacity (mAh) = battery total capacity (mAh) *
* current capacity percent (capacity / 100).
*/
int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
/*
* Convert current capacity (mAh) to coulomb counter according to the
* formula: 1 mAh =3.6 coulomb.
*/
return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
}
static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
{
struct nvmem_cell *cell;
int calib_data, cal_4200mv;
void *buf;
size_t len;
cell = nvmem_cell_get(data->dev, "fgu_calib");
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
if (IS_ERR(buf))
return PTR_ERR(buf);
memcpy(&calib_data, buf, min(len, sizeof(u32)));
/*
* Get the ADC value corresponding to 4200 mV from eFuse controller
* according to below formula. Then convert to ADC values corresponding
* to 1000 mV and 1000 mA.
*/
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;
kfree(buf);
return 0;
}
static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
{
struct power_supply_battery_info info = { };
struct power_supply_battery_ocv_table *table;
int ret, delta_clbcnt, alarm_adc;
ret = power_supply_get_battery_info(data->battery, &info);
if (ret) {
dev_err(data->dev, "failed to get battery information\n");
return ret;
}
data->total_cap = info.charge_full_design_uah / 1000;
data->max_volt = info.constant_charge_voltage_max_uv / 1000;
data->internal_resist = info.factory_internal_resistance_uohm / 1000;
data->min_volt = info.voltage_min_design_uv;
/*
* For SC27XX fuel gauge device, we only use one ocv-capacity
* table in normal temperature 20 Celsius.
*/
table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
if (!table)
return -EINVAL;
data->cap_table = devm_kmemdup(data->dev, table,
data->table_len * sizeof(*table),
GFP_KERNEL);
if (!data->cap_table) {
power_supply_put_battery_info(data->battery, &info);
return -ENOMEM;
}
data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
data->table_len,
data->min_volt);
power_supply_put_battery_info(data->battery, &info);
ret = sc27xx_fgu_calibration(data);
if (ret)
return ret;
/* Enable the FGU module */
ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
SC27XX_FGU_EN, SC27XX_FGU_EN);
if (ret) {
dev_err(data->dev, "failed to enable fgu\n");
return ret;
}
/* Enable the FGU RTC clock to make it work */
ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
if (ret) {
dev_err(data->dev, "failed to enable fgu RTC clock\n");
goto disable_fgu;
}
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
if (ret) {
dev_err(data->dev, "failed to clear interrupt status\n");
goto disable_clk;
}
/*
* Set the voltage low overload threshold, which means when the battery
* voltage is lower than this threshold, the controller will generate
* one interrupt to notify.
*/
alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
if (ret) {
dev_err(data->dev, "failed to set fgu low overload\n");
goto disable_clk;
}
/*
* Set the coulomb counter delta threshold, that means when the coulomb
* counter change is multiples of the delta threshold, the controller
* will generate one interrupt to notify the users to update the battery
* capacity. Now we set the delta threshold as a counter value of 1%
* capacity.
*/
delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
if (ret) {
dev_err(data->dev, "failed to set low delta coulomb counter\n");
goto disable_clk;
}
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
SC27XX_FGU_CLBCNT_MASK,
delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
if (ret) {
dev_err(data->dev, "failed to set high delta coulomb counter\n");
goto disable_clk;
}
/*
* Get the boot battery capacity when system powers on, which is used to
* initialize the coulomb counter. After that, we can read the coulomb
* counter to measure the battery capacity.
*/
ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
if (ret) {
dev_err(data->dev, "failed to get boot capacity\n");
goto disable_clk;
}
/*
* Convert battery capacity to the corresponding initial coulomb counter
* and set into coulomb counter registers.
*/
data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
if (ret) {
dev_err(data->dev, "failed to initialize coulomb counter\n");
goto disable_clk;
}
return 0;
disable_clk:
regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
disable_fgu:
regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
return ret;
}
static int sc27xx_fgu_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct power_supply_config fgu_cfg = { };
struct sc27xx_fgu_data *data;
int ret, irq;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!data->regmap) {
dev_err(&pdev->dev, "failed to get regmap\n");
return -ENODEV;
}
ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
if (ret) {
dev_err(&pdev->dev, "failed to get fgu address\n");
return ret;
}
data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
if (IS_ERR(data->channel)) {
dev_err(&pdev->dev, "failed to get IIO channel\n");
return PTR_ERR(data->channel);
}
data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
if (IS_ERR(data->gpiod)) {
dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
return PTR_ERR(data->gpiod);
}
ret = gpiod_get_value_cansleep(data->gpiod);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get gpio state\n");
return ret;
}
data->bat_present = !!ret;
mutex_init(&data->lock);
data->dev = &pdev->dev;
platform_set_drvdata(pdev, data);
fgu_cfg.drv_data = data;
fgu_cfg.of_node = np;
data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
&fgu_cfg);
if (IS_ERR(data->battery)) {
dev_err(&pdev->dev, "failed to register power supply\n");
return PTR_ERR(data->battery);
}
ret = sc27xx_fgu_hw_init(data);
if (ret) {
dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
return ret;
}
ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
if (ret) {
sc27xx_fgu_disable(data);
dev_err(&pdev->dev, "failed to add fgu disable action\n");
return ret;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource specified\n");
return irq;
}
ret = devm_request_threaded_irq(data->dev, irq, NULL,
sc27xx_fgu_interrupt,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
pdev->name, data);
if (ret) {
dev_err(data->dev, "failed to request fgu IRQ\n");
return ret;
}
irq = gpiod_to_irq(data->gpiod);
if (irq < 0) {
dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
return irq;
}
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
sc27xx_fgu_bat_detection,
IRQF_ONESHOT | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
pdev->name, data);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
}
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sc27xx_fgu_resume(struct device *dev)
{
struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
int ret;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
SC27XX_FGU_LOW_OVERLOAD_INT |
SC27XX_FGU_CLBCNT_DELTA_INT, 0);
if (ret) {
dev_err(data->dev, "failed to disable fgu interrupts\n");
return ret;
}
return 0;
}
static int sc27xx_fgu_suspend(struct device *dev)
{
struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
int ret, status, ocv;
ret = sc27xx_fgu_get_status(data, &status);
if (ret)
return ret;
/*
* If we are charging, then no need to enable the FGU interrupts to
* adjust the battery capacity.
*/
if (status != POWER_SUPPLY_STATUS_NOT_CHARGING)
return 0;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
SC27XX_FGU_LOW_OVERLOAD_INT,
SC27XX_FGU_LOW_OVERLOAD_INT);
if (ret) {
dev_err(data->dev, "failed to enable low voltage interrupt\n");
return ret;
}
ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
if (ret)
goto disable_int;
/*
* If current OCV is less than the minimum voltage, we should enable the
* coulomb counter threshold interrupt to notify events to adjust the
* battery capacity.
*/
if (ocv < data->min_volt) {
ret = regmap_update_bits(data->regmap,
data->base + SC27XX_FGU_INT_EN,
SC27XX_FGU_CLBCNT_DELTA_INT,
SC27XX_FGU_CLBCNT_DELTA_INT);
if (ret) {
dev_err(data->dev,
"failed to enable coulomb threshold int\n");
goto disable_int;
}
}
return 0;
disable_int:
regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
SC27XX_FGU_LOW_OVERLOAD_INT, 0);
return ret;
}
#endif
static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
};
static const struct of_device_id sc27xx_fgu_of_match[] = {
{ .compatible = "sprd,sc2731-fgu", },
{ }
};
static struct platform_driver sc27xx_fgu_driver = {
.probe = sc27xx_fgu_probe,
.driver = {
.name = "sc27xx-fgu",
.of_match_table = sc27xx_fgu_of_match,
.pm = &sc27xx_fgu_pm_ops,
}
};
module_platform_driver(sc27xx_fgu_driver);
MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
MODULE_LICENSE("GPL v2");
...@@ -266,6 +266,7 @@ enum axp20x_variants { ...@@ -266,6 +266,7 @@ enum axp20x_variants {
#define AXP288_RT_BATT_V_H 0xa0 #define AXP288_RT_BATT_V_H 0xa0
#define AXP288_RT_BATT_V_L 0xa1 #define AXP288_RT_BATT_V_L 0xa1
#define AXP813_ACIN_PATH_CTRL 0x3a
#define AXP813_ADC_RATE 0x85 #define AXP813_ADC_RATE 0x85
/* Fuel Gauge */ /* Fuel Gauge */
......
...@@ -119,7 +119,7 @@ struct charger_regulator { ...@@ -119,7 +119,7 @@ struct charger_regulator {
struct charger_cable *cables; struct charger_cable *cables;
int num_cables; int num_cables;
struct attribute_group attr_g; struct attribute_group attr_grp;
struct device_attribute attr_name; struct device_attribute attr_name;
struct device_attribute attr_state; struct device_attribute attr_state;
struct device_attribute attr_externally_control; struct device_attribute attr_externally_control;
...@@ -186,6 +186,7 @@ struct charger_desc { ...@@ -186,6 +186,7 @@ struct charger_desc {
int num_charger_regulators; int num_charger_regulators;
struct charger_regulator *charger_regulators; struct charger_regulator *charger_regulators;
const struct attribute_group **sysfs_groups;
const char *psy_fuel_gauge; const char *psy_fuel_gauge;
......
...@@ -204,6 +204,9 @@ struct power_supply_config { ...@@ -204,6 +204,9 @@ struct power_supply_config {
/* Driver private data */ /* Driver private data */
void *drv_data; void *drv_data;
/* Device specific sysfs attributes */
const struct attribute_group **attr_grp;
char **supplied_to; char **supplied_to;
size_t num_supplicants; size_t num_supplicants;
}; };
...@@ -309,6 +312,13 @@ struct power_supply_info { ...@@ -309,6 +312,13 @@ struct power_supply_info {
int use_for_apm; int use_for_apm;
}; };
struct power_supply_battery_ocv_table {
int ocv; /* microVolts */
int capacity; /* percent */
};
#define POWER_SUPPLY_OCV_TEMP_MAX 20
/* /*
* This is the recommended struct to manage static battery parameters, * This is the recommended struct to manage static battery parameters,
* populated by power_supply_get_battery_info(). Most platform drivers should * populated by power_supply_get_battery_info(). Most platform drivers should
...@@ -326,6 +336,10 @@ struct power_supply_battery_info { ...@@ -326,6 +336,10 @@ struct power_supply_battery_info {
int charge_term_current_ua; /* microAmps */ int charge_term_current_ua; /* microAmps */
int constant_charge_current_max_ua; /* microAmps */ int constant_charge_current_max_ua; /* microAmps */
int constant_charge_voltage_max_uv; /* microVolts */ int constant_charge_voltage_max_uv; /* microVolts */
int factory_internal_resistance_uohm; /* microOhms */
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];
}; };
extern struct atomic_notifier_head power_supply_notifier; extern struct atomic_notifier_head power_supply_notifier;
...@@ -349,6 +363,15 @@ devm_power_supply_get_by_phandle(struct device *dev, const char *property) ...@@ -349,6 +363,15 @@ devm_power_supply_get_by_phandle(struct device *dev, const char *property)
extern int power_supply_get_battery_info(struct power_supply *psy, extern int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info); struct power_supply_battery_info *info);
extern void power_supply_put_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info);
extern int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
int table_len, int ocv);
extern struct power_supply_battery_ocv_table *
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 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