Commit 4520dcbe authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power supply and reset updates from Sebastian Reichel:
 "Battery/charger related:
   - cros-peripheral-charger: new driver
   - mt6360-charger: new driver
   - simple-battery: support reading chemistry info
   - max17042-battery: add max77849 support
   - sbs-battery: add time_to_empty_now support
   - smb347-charger: prepare USB OTG support
   - rn5t618: add voltage_now support
   - axp288: cleanup & optimizations
   - max17042_battery: cleanups
   - ab8500: cleanups
   - misc minor cleanups and DT binding fixes

  reset related:
   - tps65086-restart: new driver
   - linkstation-poweroff: support NETGEAR ReadyNAS Duo v2"

* tag 'for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (51 commits)
  power: supply: core: Fix parsing of battery chemistry/technology
  power: supply: max17042_battery: log SOC threshold using debug log level
  power: supply: max17042_battery: more robust chip type checks
  power: supply: max17042_battery: fix typo in MAx17042_TOFF
  power: supply: max17042_battery: clean up MAX17055_V_empty
  power: supply: smb347-charger: Implement USB VBUS regulator
  power: supply: smb347-charger: Add missing pin control activation
  power: supply: smb347-charger: Utilize generic regmap caching
  power: supply: smb347-charger: Make smb347_set_writable() IRQ-safe
  dt-bindings: power: supply: smb347-charger: Document USB VBUS regulator
  power: reset: Add TPS65086 restart driver
  dt-bindings: power: supply: max17042: describe interrupt
  power: supply: max17042: remove duplicated STATUS bit defines
  power: supply: max17042: handle fails of reading status register
  power: supply: core: Parse battery chemistry/technology
  dt-bindings: power: Extend battery bindings with chemistry
  power: reset: linkstation-poweroff: add new device
  power: reset: linkstation-poweroff: prepare for new devices
  power: supply: bq24735: reorganize ChargeOption command macros
  power: supply: rn5t618: Add voltage_now property
  ...
parents 0da9bc6d c9398455
......@@ -31,6 +31,20 @@ properties:
compatible:
const: simple-battery
device-chemistry:
description: This describes the chemical technology of the battery.
oneOf:
- const: nickel-cadmium
- const: nickel-metal-hydride
- const: lithium-ion
description: This is a blanket type for all lithium-ion batteries,
including those below. If possible, a precise compatible string
from below should be used, but sometimes it is unknown which specific
lithium ion battery is employed and this wide compatible can be used.
- const: lithium-ion-polymer
- const: lithium-ion-iron-phosphate
- const: lithium-ion-manganese-oxide
over-voltage-threshold-microvolt:
description: battery over-voltage limit
......
......@@ -19,12 +19,15 @@ properties:
- maxim,max17047
- maxim,max17050
- maxim,max17055
- maxim,max77849-battery
reg:
maxItems: 1
interrupts:
maxItems: 1
description: |
The ALRT pin, an open-drain interrupt.
maxim,rsns-microohm:
$ref: /schemas/types.yaml#/definitions/uint32
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/mt6360_charger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Battery charger driver for MT6360 PMIC from MediaTek Integrated.
maintainers:
- Gene Chen <gene_chen@richtek.com>
description: |
This module is part of the MT6360 MFD device.
Provides Battery Charger, Boost for OTG devices and BC1.2 detection.
properties:
compatible:
const: mediatek,mt6360-chg
richtek,vinovp-microvolt:
description: Maximum CHGIN regulation voltage in uV.
enum: [ 5500000, 6500000, 11000000, 14500000 ]
usb-otg-vbus-regulator:
type: object
description: OTG boost regulator.
$ref: /schemas/regulator/regulator.yaml#
required:
- compatible
additionalProperties: false
examples:
- |
mt6360_charger: charger {
compatible = "mediatek,mt6360-chg";
richtek,vinovp-microvolt = <14500000>;
otg_vbus_regulator: usb-otg-vbus-regulator {
regulator-compatible = "usb-otg-vbus";
regulator-name = "usb-otg-vbus";
regulator-min-microvolt = <4425000>;
regulator-max-microvolt = <5825000>;
};
};
...
......@@ -73,6 +73,26 @@ properties:
- 1 # SMB3XX_SOFT_TEMP_COMPENSATE_CURRENT Current compensation
- 2 # SMB3XX_SOFT_TEMP_COMPENSATE_VOLTAGE Voltage compensation
summit,inok-polarity:
description: |
Polarity of INOK signal indicating presence of external power supply.
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # SMB3XX_SYSOK_INOK_ACTIVE_LOW
- 1 # SMB3XX_SYSOK_INOK_ACTIVE_HIGH
usb-vbus:
$ref: "../../regulator/regulator.yaml#"
type: object
properties:
summit,needs-inok-toggle:
type: boolean
description: INOK signal is fixed and polarity needs to be toggled
in order to enable/disable output mode.
unevaluatedProperties: false
allOf:
- if:
properties:
......@@ -134,6 +154,7 @@ examples:
reg = <0x7f>;
summit,enable-charge-control = <SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH>;
summit,inok-polarity = <SMB3XX_SYSOK_INOK_ACTIVE_LOW>;
summit,chip-temperature-threshold-celsius = <110>;
summit,mains-current-limit-microamp = <2000000>;
summit,usb-current-limit-microamp = <500000>;
......@@ -141,6 +162,15 @@ examples:
summit,enable-mains-charging;
monitored-battery = <&battery>;
usb-vbus {
regulator-name = "usb_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-min-microamp = <750000>;
regulator-max-microamp = <750000>;
summit,needs-inok-toggle;
};
};
};
......
......@@ -21,10 +21,13 @@ allOf:
properties:
compatible:
enum:
- x-powers,axp202-ac-power-supply
- x-powers,axp221-ac-power-supply
- x-powers,axp813-ac-power-supply
oneOf:
- const: x-powers,axp202-ac-power-supply
- const: x-powers,axp221-ac-power-supply
- items:
- const: x-powers,axp803-ac-power-supply
- const: x-powers,axp813-ac-power-supply
- const: x-powers,axp813-ac-power-supply
required:
- compatible
......
......@@ -19,10 +19,14 @@ allOf:
properties:
compatible:
enum:
- x-powers,axp209-battery-power-supply
- x-powers,axp221-battery-power-supply
- x-powers,axp813-battery-power-supply
oneOf:
- const: x-powers,axp202-battery-power-supply
- const: x-powers,axp209-battery-power-supply
- const: x-powers,axp221-battery-power-supply
- items:
- const: x-powers,axp803-battery-power-supply
- const: x-powers,axp813-battery-power-supply
- const: x-powers,axp813-battery-power-supply
required:
- compatible
......
......@@ -20,11 +20,15 @@ allOf:
properties:
compatible:
enum:
- x-powers,axp202-usb-power-supply
- x-powers,axp221-usb-power-supply
- x-powers,axp223-usb-power-supply
- x-powers,axp813-usb-power-supply
oneOf:
- enum:
- x-powers,axp202-usb-power-supply
- x-powers,axp221-usb-power-supply
- x-powers,axp223-usb-power-supply
- x-powers,axp813-usb-power-supply
- items:
- const: x-powers,axp803-usb-power-supply
- const: x-powers,axp813-usb-power-supply
required:
......
......@@ -16,6 +16,8 @@
#include <linux/completion.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
#include <linux/slab.h>
#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500))
......@@ -189,6 +191,19 @@ static const struct iio_chan_spec rn5t618_adc_iio_channels[] = {
RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0")
};
static struct iio_map rn5t618_maps[] = {
IIO_MAP("VADP", "rn5t618-power", "vadp"),
IIO_MAP("VUSB", "rn5t618-power", "vusb"),
{ /* sentinel */ }
};
static void unregister_map(void *data)
{
struct iio_dev *iio_dev = (struct iio_dev *) data;
iio_map_array_unregister(iio_dev);
}
static int rn5t618_adc_probe(struct platform_device *pdev)
{
int ret;
......@@ -239,6 +254,14 @@ static int rn5t618_adc_probe(struct platform_device *pdev)
return ret;
}
ret = iio_map_array_register(iio_dev, rn5t618_maps);
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(adc->dev, unregister_map, iio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(adc->dev, iio_dev);
}
......
......@@ -204,6 +204,12 @@ config POWER_RESET_ST
help
Reset support for STMicroelectronics boards.
config POWER_RESET_TPS65086
bool "TPS65086 restart driver"
depends on MFD_TPS65086
help
This driver adds support for resetting the TPS65086 PMIC on restart.
config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver"
depends on ARM
......
......@@ -23,6 +23,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
......
......@@ -19,6 +19,7 @@
#define MII_MARVELL_PHY_PAGE 22
#define MII_PHY_LED_CTRL 16
#define MII_PHY_LED_POL_CTRL 17
#define MII_88E1318S_PHY_LED_TCR 18
#define MII_88E1318S_PHY_WOL_CTRL 16
#define MII_M1011_IEVENT 19
......@@ -29,11 +30,23 @@
#define LED2_FORCE_ON (0x8 << 8)
#define LEDMASK GENMASK(11,8)
#define MII_88E1318S_PHY_LED_POL_LED2 BIT(4)
struct power_off_cfg {
char *mdio_node_name;
void (*phy_set_reg)(bool restart);
};
static struct phy_device *phydev;
static const struct power_off_cfg *cfg;
static void mvphy_reg_intn(u16 data)
static void linkstation_mvphy_reg_intn(bool restart)
{
int rc = 0, saved_page;
u16 data = 0;
if (restart)
data = MII_88E1318S_PHY_LED_TCR_FORCE_INT;
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
if (saved_page < 0)
......@@ -66,11 +79,52 @@ static void mvphy_reg_intn(u16 data)
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
}
static void readynas_mvphy_set_reg(bool restart)
{
int rc = 0, saved_page;
u16 data = 0;
if (restart)
data = MII_88E1318S_PHY_LED_POL_LED2;
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
if (saved_page < 0)
goto err;
/* Set the LED[2].0 Polarity bit to the required state */
__phy_modify(phydev, MII_PHY_LED_POL_CTRL,
MII_88E1318S_PHY_LED_POL_LED2, data);
if (!data) {
/* If WOL was enabled and a magic packet was received before powering
* off, we won't be able to wake up by sending another magic packet.
* Clear WOL status.
*/
__phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE);
__phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
}
err:
rc = phy_restore_page(phydev, saved_page, rc);
if (rc < 0)
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
}
static const struct power_off_cfg linkstation_power_off_cfg = {
.mdio_node_name = "mdio",
.phy_set_reg = linkstation_mvphy_reg_intn,
};
static const struct power_off_cfg readynas_power_off_cfg = {
.mdio_node_name = "mdio-bus",
.phy_set_reg = readynas_mvphy_set_reg,
};
static int linkstation_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused)
{
if (action == SYS_RESTART)
mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT);
cfg->phy_set_reg(true);
return NOTIFY_DONE;
}
......@@ -82,14 +136,21 @@ static struct notifier_block linkstation_reboot_nb = {
static void linkstation_poweroff(void)
{
unregister_reboot_notifier(&linkstation_reboot_nb);
mvphy_reg_intn(0);
cfg->phy_set_reg(false);
kernel_restart("Power off");
}
static const struct of_device_id ls_poweroff_of_match[] = {
{ .compatible = "buffalo,ls421d" },
{ .compatible = "buffalo,ls421de" },
{ .compatible = "buffalo,ls421d",
.data = &linkstation_power_off_cfg,
},
{ .compatible = "buffalo,ls421de",
.data = &linkstation_power_off_cfg,
},
{ .compatible = "netgear,readynas-duo-v2",
.data = &readynas_power_off_cfg,
},
{ },
};
......@@ -97,13 +158,17 @@ static int __init linkstation_poweroff_init(void)
{
struct mii_bus *bus;
struct device_node *dn;
const struct of_device_id *match;
dn = of_find_matching_node(NULL, ls_poweroff_of_match);
if (!dn)
return -ENODEV;
of_node_put(dn);
dn = of_find_node_by_name(NULL, "mdio");
match = of_match_node(ls_poweroff_of_match, dn);
cfg = match->data;
dn = of_find_node_by_name(NULL, cfg->mdio_node_name);
if (!dn)
return -ENODEV;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Emil Renner Berthing
*/
#include <linux/mfd/tps65086.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
struct tps65086_restart {
struct notifier_block handler;
struct device *dev;
};
static int tps65086_restart_notify(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct tps65086_restart *tps65086_restart =
container_of(this, struct tps65086_restart, handler);
struct tps65086 *tps65086 = dev_get_drvdata(tps65086_restart->dev->parent);
int ret;
ret = regmap_write(tps65086->regmap, TPS65086_FORCESHUTDN, 1);
if (ret) {
dev_err(tps65086_restart->dev, "%s: error writing to tps65086 pmic: %d\n",
__func__, ret);
return NOTIFY_DONE;
}
/* give it a little time */
mdelay(200);
WARN_ON(1);
return NOTIFY_DONE;
}
static int tps65086_restart_probe(struct platform_device *pdev)
{
struct tps65086_restart *tps65086_restart;
int ret;
tps65086_restart = devm_kzalloc(&pdev->dev, sizeof(*tps65086_restart), GFP_KERNEL);
if (!tps65086_restart)
return -ENOMEM;
platform_set_drvdata(pdev, tps65086_restart);
tps65086_restart->handler.notifier_call = tps65086_restart_notify;
tps65086_restart->handler.priority = 192;
tps65086_restart->dev = &pdev->dev;
ret = register_restart_handler(&tps65086_restart->handler);
if (ret) {
dev_err(&pdev->dev, "%s: cannot register restart handler: %d\n",
__func__, ret);
return -ENODEV;
}
return 0;
}
static int tps65086_restart_remove(struct platform_device *pdev)
{
struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev);
int ret;
ret = unregister_restart_handler(&tps65086_restart->handler);
if (ret) {
dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n",
__func__, ret);
return -ENODEV;
}
return 0;
}
static const struct platform_device_id tps65086_restart_id_table[] = {
{ "tps65086-reset", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table);
static struct platform_driver tps65086_restart_driver = {
.driver = {
.name = "tps65086-restart",
},
.probe = tps65086_restart_probe,
.remove = tps65086_restart_remove,
.id_table = tps65086_restart_id_table,
};
module_platform_driver(tps65086_restart_driver);
MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>");
MODULE_DESCRIPTION("TPS65086 restart driver");
MODULE_LICENSE("GPL v2");
......@@ -358,7 +358,7 @@ config AXP288_CHARGER
config AXP288_FUEL_GAUGE
tristate "X-Powers AXP288 Fuel Gauge"
depends on MFD_AXP20X && IIO
depends on MFD_AXP20X && IIO && IOSF_MBI
help
Say yes here to have support for X-Power power management IC (PMIC)
Fuel Gauge. The device provides battery statistics and status
......@@ -577,6 +577,17 @@ config CHARGER_MP2629
Battery charger. This driver provides Battery charger power management
functions on the systems.
config CHARGER_MT6360
tristate "Mediatek MT6360 Charger Driver"
depends on MFD_MT6360
depends on REGULATOR
select LINEAR_RANGES
help
Say Y here to enable MT6360 Charger Part.
The device supports High-Accuracy Voltage/Current Regulation,
Average Input Current Regulation, Battery Temperature Sensing,
Over-Temperature Protection, DPDM Detection for BC1.2.
config CHARGER_QCOM_SMBB
tristate "Qualcomm Switch-Mode Battery Charger and Boost"
depends on MFD_SPMI_PMIC || COMPILE_TEST
......@@ -669,6 +680,7 @@ config CHARGER_BQ256XX
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
depends on REGULATOR
select REGMAP_I2C
help
Say Y to include support for Summit Microelectronics SMB345,
......@@ -736,6 +748,16 @@ config CHARGER_CROS_USBPD
what is connected to USB PD ports from the EC and converts
that into power_supply properties.
config CHARGER_CROS_PCHG
tristate "ChromeOS EC based peripheral charger"
depends on MFD_CROS_EC_DEV
default MFD_CROS_EC_DEV
help
Say Y here to enable ChromeOS EC based peripheral charge driver.
This driver gets various information about the devices connected to
the peripheral charge ports from the EC and converts that into
power_supply properties.
config CHARGER_SC2731
tristate "Spreadtrum SC2731 charger driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
......@@ -782,6 +804,8 @@ config CHARGER_WILCO
config RN5T618_POWER
tristate "RN5T618 charger/fuel gauge support"
depends on MFD_RN5T618
depends on RN5T618_ADC
depends on IIO
help
Say Y here to have support for RN5T618 PMIC family fuel gauge and charger.
This driver can also be built as a module. If so, the module will be
......
......@@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
......@@ -78,6 +78,7 @@ obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o
obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o
obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
......@@ -93,6 +94,7 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
......
......@@ -269,43 +269,43 @@ enum bup_vch_sel {
/*
* ADC for the battery thermistor.
* When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined
* When using the AB8500_ADC_THERM_BATCTRL the battery ID resistor is combined
* with a NTC resistor to both identify the battery and to measure its
* temperature. Different phone manufactures uses different techniques to both
* identify the battery and to read its temperature.
*/
enum abx500_adc_therm {
ABx500_ADC_THERM_BATCTRL,
ABx500_ADC_THERM_BATTEMP,
enum ab8500_adc_therm {
AB8500_ADC_THERM_BATCTRL,
AB8500_ADC_THERM_BATTEMP,
};
/**
* struct abx500_res_to_temp - defines one point in a temp to res curve. To
* struct ab8500_res_to_temp - defines one point in a temp to res curve. To
* be used in battery packs that combines the identification resistor with a
* NTC resistor.
* @temp: battery pack temperature in Celsius
* @resist: NTC resistor net total resistance
*/
struct abx500_res_to_temp {
struct ab8500_res_to_temp {
int temp;
int resist;
};
/**
* struct abx500_v_to_cap - Table for translating voltage to capacity
* struct ab8500_v_to_cap - Table for translating voltage to capacity
* @voltage: Voltage in mV
* @capacity: Capacity in percent
*/
struct abx500_v_to_cap {
struct ab8500_v_to_cap {
int voltage;
int capacity;
};
/* Forward declaration */
struct abx500_fg;
struct ab8500_fg;
/**
* struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds
* struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
* if not specified
* @recovery_sleep_timer: Time between measurements while recovering
* @recovery_total_time: Total recovery time
......@@ -333,7 +333,7 @@ struct abx500_fg;
* @pcut_max_restart: Max number of restarts
* @pcut_debounce_time: Sets battery debounce time
*/
struct abx500_fg_parameters {
struct ab8500_fg_parameters {
int recovery_sleep_timer;
int recovery_total_time;
int init_timer;
......@@ -357,13 +357,13 @@ struct abx500_fg_parameters {
};
/**
* struct abx500_charger_maximization - struct used by the board config.
* struct ab8500_charger_maximization - struct used by the board config.
* @use_maxi: Enable maximization for this battery type
* @maxi_chg_curr: Maximum charger current allowed
* @maxi_wait_cycles: cycles to wait before setting charger current
* @charger_curr_step delta between two charger current settings (mA)
*/
struct abx500_maxim_parameters {
struct ab8500_maxim_parameters {
bool ena_maxi;
int chg_curr;
int wait_cycles;
......@@ -371,7 +371,7 @@ struct abx500_maxim_parameters {
};
/**
* struct abx500_battery_type - different batteries supported
* struct ab8500_battery_type - different batteries supported
* @name: battery technology
* @resis_high: battery upper resistance limit
* @resis_low: battery lower resistance limit
......@@ -400,7 +400,7 @@ struct abx500_maxim_parameters {
* @n_batres_tbl_elements number of elements in the batres_tbl
* @batres_tbl battery internal resistance vs temperature table
*/
struct abx500_battery_type {
struct ab8500_battery_type {
int name;
int resis_high;
int resis_low;
......@@ -421,22 +421,22 @@ struct abx500_battery_type {
int low_high_vol_lvl;
int battery_resistance;
int n_temp_tbl_elements;
const struct abx500_res_to_temp *r_to_t_tbl;
const struct ab8500_res_to_temp *r_to_t_tbl;
int n_v_cap_tbl_elements;
const struct abx500_v_to_cap *v_to_cap_tbl;
const struct ab8500_v_to_cap *v_to_cap_tbl;
int n_batres_tbl_elements;
const struct batres_vs_temp *batres_tbl;
};
/**
* struct abx500_bm_capacity_levels - abx500 capacity level data
* struct ab8500_bm_capacity_levels - ab8500 capacity level data
* @critical: critical capacity level in percent
* @low: low capacity level in percent
* @normal: normal capacity level in percent
* @high: high capacity level in percent
* @full: full capacity level in percent
*/
struct abx500_bm_capacity_levels {
struct ab8500_bm_capacity_levels {
int critical;
int low;
int normal;
......@@ -445,13 +445,13 @@ struct abx500_bm_capacity_levels {
};
/**
* struct abx500_bm_charger_parameters - Charger specific parameters
* struct ab8500_bm_charger_parameters - Charger specific parameters
* @usb_volt_max: maximum allowed USB charger voltage in mV
* @usb_curr_max: maximum allowed USB charger current in mA
* @ac_volt_max: maximum allowed AC charger voltage in mV
* @ac_curr_max: maximum allowed AC charger current in mA
*/
struct abx500_bm_charger_parameters {
struct ab8500_bm_charger_parameters {
int usb_volt_max;
int usb_curr_max;
int ac_volt_max;
......@@ -459,7 +459,7 @@ struct abx500_bm_charger_parameters {
};
/**
* struct abx500_bm_data - abx500 battery management data
* struct ab8500_bm_data - ab8500 battery management data
* @temp_under under this temp, charging is stopped
* @temp_low between this temp and temp_under charging is reduced
* @temp_high between this temp and temp_over charging is reduced
......@@ -473,7 +473,7 @@ struct abx500_bm_charger_parameters {
* @bkup_bat_i current which we charge the backup battery with
* @no_maintenance indicates that maintenance charging is disabled
* @capacity_scaling indicates whether capacity scaling is to be used
* @abx500_adc_therm placement of thermistor, batctrl or battemp adc
* @ab8500_adc_therm placement of thermistor, batctrl or battemp adc
* @chg_unknown_bat flag to enable charging of unknown batteries
* @enable_overshoot flag to enable VBAT overshoot control
* @auto_trig flag to enable auto adc trigger
......@@ -494,7 +494,7 @@ struct abx500_bm_charger_parameters {
* @chg_params charger parameters
* @fg_params fuel gauge parameters
*/
struct abx500_bm_data {
struct ab8500_bm_data {
int temp_under;
int temp_low;
int temp_high;
......@@ -511,7 +511,7 @@ struct abx500_bm_data {
bool chg_unknown_bat;
bool enable_overshoot;
bool auto_trig;
enum abx500_adc_therm adc_therm;
enum ab8500_adc_therm adc_therm;
int fg_res;
int n_btypes;
int batt_id;
......@@ -523,11 +523,11 @@ struct abx500_bm_data {
int n_chg_in_curr;
int *chg_output_curr;
int *chg_input_curr;
const struct abx500_maxim_parameters *maxi;
const struct abx500_bm_capacity_levels *cap_levels;
struct abx500_battery_type *bat_type;
const struct abx500_bm_charger_parameters *chg_params;
const struct abx500_fg_parameters *fg_params;
const struct ab8500_maxim_parameters *maxi;
const struct ab8500_bm_capacity_levels *cap_levels;
struct ab8500_battery_type *bat_type;
const struct ab8500_bm_charger_parameters *chg_params;
const struct ab8500_fg_parameters *fg_params;
};
enum {
......@@ -561,160 +561,7 @@ struct batres_vs_temp {
/* Forward declaration */
struct ab8500_fg;
/**
* struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
* if not specified
* @recovery_sleep_timer: Time between measurements while recovering
* @recovery_total_time: Total recovery time
* @init_timer: Measurement interval during startup
* @init_discard_time: Time we discard voltage measurement at startup
* @init_total_time: Total init time during startup
* @high_curr_time: Time current has to be high to go to recovery
* @accu_charging: FG accumulation time while charging
* @accu_high_curr: FG accumulation time in high current mode
* @high_curr_threshold: High current threshold, in mA
* @lowbat_threshold: Low battery threshold, in mV
* @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
* Resolution in 50 mV step.
* @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
* Resolution in 50 mV step.
* @user_cap_limit Capacity reported from user must be within this
* limit to be considered as sane, in percentage
* points.
* @maint_thres This is the threshold where we stop reporting
* battery full while in maintenance, in per cent
* @pcut_enable: Enable power cut feature in ab8505
* @pcut_max_time: Max time threshold
* @pcut_flag_time: Flagtime threshold
* @pcut_max_restart: Max number of restarts
* @pcut_debunce_time: Sets battery debounce time
*/
struct ab8500_fg_parameters {
int recovery_sleep_timer;
int recovery_total_time;
int init_timer;
int init_discard_time;
int init_total_time;
int high_curr_time;
int accu_charging;
int accu_high_curr;
int high_curr_threshold;
int lowbat_threshold;
int battok_falling_th_sel0;
int battok_raising_th_sel1;
int user_cap_limit;
int maint_thres;
bool pcut_enable;
u8 pcut_max_time;
u8 pcut_flag_time;
u8 pcut_max_restart;
u8 pcut_debunce_time;
};
/**
* struct ab8500_charger_maximization - struct used by the board config.
* @use_maxi: Enable maximization for this battery type
* @maxi_chg_curr: Maximum charger current allowed
* @maxi_wait_cycles: cycles to wait before setting charger current
* @charger_curr_step delta between two charger current settings (mA)
*/
struct ab8500_maxim_parameters {
bool ena_maxi;
int chg_curr;
int wait_cycles;
int charger_curr_step;
};
/**
* struct ab8500_bm_capacity_levels - ab8500 capacity level data
* @critical: critical capacity level in percent
* @low: low capacity level in percent
* @normal: normal capacity level in percent
* @high: high capacity level in percent
* @full: full capacity level in percent
*/
struct ab8500_bm_capacity_levels {
int critical;
int low;
int normal;
int high;
int full;
};
/**
* struct ab8500_bm_charger_parameters - Charger specific parameters
* @usb_volt_max: maximum allowed USB charger voltage in mV
* @usb_curr_max: maximum allowed USB charger current in mA
* @ac_volt_max: maximum allowed AC charger voltage in mV
* @ac_curr_max: maximum allowed AC charger current in mA
*/
struct ab8500_bm_charger_parameters {
int usb_volt_max;
int usb_curr_max;
int ac_volt_max;
int ac_curr_max;
};
/**
* struct ab8500_bm_data - ab8500 battery management data
* @temp_under under this temp, charging is stopped
* @temp_low between this temp and temp_under charging is reduced
* @temp_high between this temp and temp_over charging is reduced
* @temp_over over this temp, charging is stopped
* @temp_interval_chg temperature measurement interval in s when charging
* @temp_interval_nochg temperature measurement interval in s when not charging
* @main_safety_tmr_h safety timer for main charger
* @usb_safety_tmr_h safety timer for usb charger
* @bkup_bat_v voltage which we charge the backup battery with
* @bkup_bat_i current which we charge the backup battery with
* @no_maintenance indicates that maintenance charging is disabled
* @capacity_scaling indicates whether capacity scaling is to be used
* @adc_therm placement of thermistor, batctrl or battemp adc
* @chg_unknown_bat flag to enable charging of unknown batteries
* @enable_overshoot flag to enable VBAT overshoot control
* @fg_res resistance of FG resistor in 0.1mOhm
* @n_btypes number of elements in array bat_type
* @batt_id index of the identified battery in array bat_type
* @interval_charging charge alg cycle period time when charging (sec)
* @interval_not_charging charge alg cycle period time when not charging (sec)
* @temp_hysteresis temperature hysteresis
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
* @maxi: maximization parameters
* @cap_levels capacity in percent for the different capacity levels
* @bat_type table of supported battery types
* @chg_params charger parameters
* @fg_params fuel gauge parameters
*/
struct ab8500_bm_data {
int temp_under;
int temp_low;
int temp_high;
int temp_over;
int temp_interval_chg;
int temp_interval_nochg;
int main_safety_tmr_h;
int usb_safety_tmr_h;
int bkup_bat_v;
int bkup_bat_i;
bool no_maintenance;
bool capacity_scaling;
bool chg_unknown_bat;
bool enable_overshoot;
enum abx500_adc_therm adc_therm;
int fg_res;
int n_btypes;
int batt_id;
int interval_charging;
int interval_not_charging;
int temp_hysteresis;
int gnd_lift_resistance;
const struct ab8500_maxim_parameters *maxi;
const struct ab8500_bm_capacity_levels *cap_levels;
const struct ab8500_bm_charger_parameters *chg_params;
const struct ab8500_fg_parameters *fg_params;
};
extern struct abx500_bm_data ab8500_bm_data;
extern struct ab8500_bm_data ab8500_bm_data;
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
struct ab8500_fg *ab8500_fg_get(void);
......@@ -725,10 +572,10 @@ int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
struct abx500_bm_data *bm);
struct ab8500_bm_data *bm);
extern struct platform_driver ab8500_fg_driver;
extern struct platform_driver ab8500_btemp_driver;
extern struct platform_driver abx500_chargalg_driver;
extern struct platform_driver ab8500_chargalg_driver;
#endif /* _AB8500_CHARGER_H_ */
......@@ -2,8 +2,6 @@
#include <linux/export.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include "ab8500-bm.h"
......@@ -13,7 +11,7 @@
* Note that the res_to_temp table must be strictly sorted by falling resistance
* values to work.
*/
const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
const struct ab8500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
{-5, 53407},
{ 0, 48594},
{ 5, 43804},
......@@ -35,7 +33,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
const struct ab8500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
{-5, 200000},
{ 0, 159024},
{ 5, 151921},
......@@ -57,7 +55,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
static const struct ab8500_v_to_cap cap_tbl_a_thermistor[] = {
{4171, 100},
{4114, 95},
{4009, 83},
......@@ -80,7 +78,7 @@ static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
{3247, 0},
};
static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
static const struct ab8500_v_to_cap cap_tbl_b_thermistor[] = {
{4161, 100},
{4124, 98},
{4044, 90},
......@@ -103,7 +101,7 @@ static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
{3250, 0},
};
static const struct abx500_v_to_cap cap_tbl[] = {
static const struct ab8500_v_to_cap cap_tbl[] = {
{4186, 100},
{4163, 99},
{4114, 95},
......@@ -134,7 +132,7 @@ static const struct abx500_v_to_cap cap_tbl[] = {
* Note that the res_to_temp table must be strictly sorted by falling
* resistance values to work.
*/
static const struct abx500_res_to_temp temp_tbl[] = {
static const struct ab8500_res_to_temp temp_tbl[] = {
{-5, 214834},
{ 0, 162943},
{ 5, 124820},
......@@ -191,7 +189,7 @@ static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
{-20, 180},
};
static struct abx500_battery_type bat_type_thermistor[] = {
static struct ab8500_battery_type bat_type_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
......@@ -277,7 +275,7 @@ static struct abx500_battery_type bat_type_thermistor[] = {
},
};
static struct abx500_battery_type bat_type_ext_thermistor[] = {
static struct ab8500_battery_type bat_type_ext_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
......@@ -394,7 +392,7 @@ static struct abx500_battery_type bat_type_ext_thermistor[] = {
},
};
static const struct abx500_bm_capacity_levels cap_levels = {
static const struct ab8500_bm_capacity_levels cap_levels = {
.critical = 2,
.low = 10,
.normal = 70,
......@@ -402,7 +400,7 @@ static const struct abx500_bm_capacity_levels cap_levels = {
.full = 100,
};
static const struct abx500_fg_parameters fg = {
static const struct ab8500_fg_parameters fg = {
.recovery_sleep_timer = 10,
.recovery_total_time = 100,
.init_timer = 1,
......@@ -424,14 +422,14 @@ static const struct abx500_fg_parameters fg = {
.pcut_debounce_time = 2,
};
static const struct abx500_maxim_parameters ab8500_maxi_params = {
static const struct ab8500_maxim_parameters ab8500_maxi_params = {
.ena_maxi = true,
.chg_curr = 910,
.wait_cycles = 10,
.charger_curr_step = 100,
};
static const struct abx500_bm_charger_parameters chg = {
static const struct ab8500_bm_charger_parameters chg = {
.usb_volt_max = 5500,
.usb_curr_max = 1500,
.ac_volt_max = 7500,
......@@ -456,7 +454,7 @@ static int ab8500_charge_input_curr_map[] = {
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
};
struct abx500_bm_data ab8500_bm_data = {
struct ab8500_bm_data ab8500_bm_data = {
.temp_under = 3,
.temp_low = 8,
.temp_high = 43,
......@@ -469,7 +467,7 @@ struct abx500_bm_data ab8500_bm_data = {
.bkup_bat_i = BUP_ICH_SEL_150UA,
.no_maintenance = false,
.capacity_scaling = false,
.adc_therm = ABx500_ADC_THERM_BATCTRL,
.adc_therm = AB8500_ADC_THERM_BATCTRL,
.chg_unknown_bat = false,
.enable_overshoot = false,
.fg_res = 100,
......@@ -492,7 +490,7 @@ struct abx500_bm_data ab8500_bm_data = {
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
struct abx500_bm_data *bm)
struct ab8500_bm_data *bm)
{
const struct batres_vs_temp *tmp_batres_tbl;
struct device_node *battery_node;
......@@ -531,7 +529,7 @@ int ab8500_bm_of_probe(struct device *dev,
} else {
bm->n_btypes = 4;
bm->bat_type = bat_type_ext_thermistor;
bm->adc_therm = ABx500_ADC_THERM_BATTEMP;
bm->adc_therm = AB8500_ADC_THERM_BATTEMP;
tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
}
......
......@@ -27,6 +27,7 @@
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/iio/consumer.h>
#include <linux/fixp-arith.h>
#include "ab8500-bm.h"
......@@ -102,7 +103,7 @@ struct ab8500_btemp {
struct iio_channel *btemp_ball;
struct iio_channel *bat_ctrl;
struct ab8500_fg *fg;
struct abx500_bm_data *bm;
struct ab8500_bm_data *bm;
struct power_supply *btemp_psy;
struct ab8500_btemp_events events;
struct ab8500_btemp_ranges btemp_ranges;
......@@ -144,7 +145,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
return (450000 * (v_batctrl)) / (1800 - v_batctrl);
}
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) {
/*
* If the battery has internal NTC, we use the current
* source to calculate the resistance.
......@@ -206,7 +207,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
return 0;
/* Only do this for batteries with internal NTC */
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && enable) {
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
curr = BAT_CTRL_7U_ENA;
......@@ -239,7 +240,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
__func__);
goto disable_curr_source;
}
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
} else if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && !enable) {
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
/* Write 0 to the curr bits */
......@@ -417,7 +418,7 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
* based on the NTC resistance.
*/
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
const struct abx500_res_to_temp *tbl, int tbl_size, int res)
const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
{
int i;
/*
......@@ -437,8 +438,9 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
i++;
}
return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
tbl[i + 1].resist, tbl[i + 1].temp,
res);
}
/**
......@@ -456,7 +458,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
id = di->bm->batt_id;
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
id != BATTERY_UNKNOWN) {
rbat = ab8500_btemp_get_batctrl_res(di);
......@@ -525,7 +527,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
dev_dbg(di->dev, "Battery detected on %s"
" low %d < res %d < high: %d"
" index: %d\n",
di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ?
"BATCTRL" : "BATTEMP",
di->bm->bat_type[i].resis_low, res,
di->bm->bat_type[i].resis_high, i);
......@@ -545,7 +547,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
* We only have to change current source if the
* detected type is Type 1.
*/
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
di->bm->batt_id == 1) {
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
......
......@@ -3,7 +3,7 @@
* Copyright (C) ST-Ericsson SA 2012
* Copyright (c) 2012 Sony Mobile Communications AB
*
* Charging algorithm driver for abx500 variants
* Charging algorithm driver for AB8500
*
* Authors:
* Johan Palsson <johan.palsson@stericsson.com>
......@@ -49,18 +49,18 @@
#define CHARGALG_CURR_STEP_LOW 0
#define CHARGALG_CURR_STEP_HIGH 100
enum abx500_chargers {
enum ab8500_chargers {
NO_CHG,
AC_CHG,
USB_CHG,
};
struct abx500_chargalg_charger_info {
enum abx500_chargers conn_chg;
enum abx500_chargers prev_conn_chg;
enum abx500_chargers online_chg;
enum abx500_chargers prev_online_chg;
enum abx500_chargers charger_type;
struct ab8500_chargalg_charger_info {
enum ab8500_chargers conn_chg;
enum ab8500_chargers prev_conn_chg;
enum ab8500_chargers online_chg;
enum ab8500_chargers prev_online_chg;
enum ab8500_chargers charger_type;
bool usb_chg_ok;
bool ac_chg_ok;
int usb_volt;
......@@ -73,18 +73,18 @@ struct abx500_chargalg_charger_info {
int ac_iset;
};
struct abx500_chargalg_suspension_status {
struct ab8500_chargalg_suspension_status {
bool suspended_change;
bool ac_suspended;
bool usb_suspended;
};
struct abx500_chargalg_current_step_status {
struct ab8500_chargalg_current_step_status {
bool curr_step_change;
int curr_step;
};
struct abx500_chargalg_battery_data {
struct ab8500_chargalg_battery_data {
int temp;
int volt;
int avg_curr;
......@@ -92,7 +92,7 @@ struct abx500_chargalg_battery_data {
int percent;
};
enum abx500_chargalg_states {
enum ab8500_chargalg_states {
STATE_HANDHELD_INIT,
STATE_HANDHELD,
STATE_CHG_NOT_OK_INIT,
......@@ -123,7 +123,7 @@ enum abx500_chargalg_states {
STATE_WD_EXPIRED,
};
static const char *states[] = {
static const char * const states[] = {
"HANDHELD_INIT",
"HANDHELD",
"CHG_NOT_OK_INIT",
......@@ -154,7 +154,7 @@ static const char *states[] = {
"WD_EXPIRED",
};
struct abx500_chargalg_events {
struct ab8500_chargalg_events {
bool batt_unknown;
bool mainextchnotok;
bool batt_ovv;
......@@ -176,7 +176,7 @@ struct abx500_chargalg_events {
};
/**
* struct abx500_charge_curr_maximization - Charger maximization parameters
* struct ab8500_charge_curr_maximization - Charger maximization parameters
* @original_iset: the non optimized/maximised charger current
* @current_iset: the charging current used at this moment
* @test_delta_i: the delta between the current we want to charge and the
......@@ -190,7 +190,7 @@ struct abx500_chargalg_events {
* @level: tells in how many steps the charging current has been
increased
*/
struct abx500_charge_curr_maximization {
struct ab8500_charge_curr_maximization {
int original_iset;
int current_iset;
int test_delta_i;
......@@ -207,7 +207,7 @@ enum maxim_ret {
};
/**
* struct abx500_chargalg - abx500 Charging algorithm device information
* struct ab8500_chargalg - ab8500 Charging algorithm device information
* @dev: pointer to the structure device
* @charge_status: battery operating status
* @eoc_cnt: counter used to determine end-of_charge
......@@ -223,7 +223,7 @@ enum maxim_ret {
* @susp_status: current charger suspension status
* @bm: Platform specific battery management information
* @curr_status: Current step status for over-current protection
* @parent: pointer to the struct abx500
* @parent: pointer to the struct ab8500
* @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm
* @events: structure for information about events triggered
......@@ -235,25 +235,25 @@ enum maxim_ret {
* @maintenance_timer: maintenance charging timer
* @chargalg_kobject: structure of type kobject
*/
struct abx500_chargalg {
struct ab8500_chargalg {
struct device *dev;
int charge_status;
int eoc_cnt;
bool maintenance_chg;
int t_hyst_norm;
int t_hyst_lowhigh;
enum abx500_chargalg_states charge_state;
struct abx500_charge_curr_maximization ccm;
struct abx500_chargalg_charger_info chg_info;
struct abx500_chargalg_battery_data batt_data;
struct abx500_chargalg_suspension_status susp_status;
enum ab8500_chargalg_states charge_state;
struct ab8500_charge_curr_maximization ccm;
struct ab8500_chargalg_charger_info chg_info;
struct ab8500_chargalg_battery_data batt_data;
struct ab8500_chargalg_suspension_status susp_status;
struct ab8500 *parent;
struct abx500_chargalg_current_step_status curr_status;
struct abx500_bm_data *bm;
struct ab8500_chargalg_current_step_status curr_status;
struct ab8500_bm_data *bm;
struct power_supply *chargalg_psy;
struct ux500_charger *ac_chg;
struct ux500_charger *usb_chg;
struct abx500_chargalg_events events;
struct ab8500_chargalg_events events;
struct workqueue_struct *chargalg_wq;
struct delayed_work chargalg_periodic_work;
struct delayed_work chargalg_wd_work;
......@@ -267,28 +267,28 @@ struct abx500_chargalg {
BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
/* Main battery properties */
static enum power_supply_property abx500_chargalg_props[] = {
static enum power_supply_property ab8500_chargalg_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
};
struct abx500_chargalg_sysfs_entry {
struct ab8500_chargalg_sysfs_entry {
struct attribute attr;
ssize_t (*show)(struct abx500_chargalg *, char *);
ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
ssize_t (*show)(struct ab8500_chargalg *di, char *buf);
ssize_t (*store)(struct ab8500_chargalg *di, const char *buf, size_t length);
};
/**
* abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
* ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer
* @timer: pointer to the hrtimer structure
*
* This function gets called when the safety timer for the charger
* expires
*/
static enum hrtimer_restart
abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
ab8500_chargalg_safety_timer_expired(struct hrtimer *timer)
{
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg,
safety_timer);
dev_err(di->dev, "Safety timer expired\n");
di->events.safety_timer_expired = true;
......@@ -300,7 +300,7 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
}
/**
* abx500_chargalg_maintenance_timer_expired() - Expiration of
* ab8500_chargalg_maintenance_timer_expired() - Expiration of
* the maintenance timer
* @timer: pointer to the timer structure
*
......@@ -308,10 +308,10 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
* expires
*/
static enum hrtimer_restart
abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
ab8500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
{
struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg,
maintenance_timer);
dev_dbg(di->dev, "Maintenance timer expired\n");
......@@ -324,13 +324,13 @@ abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
}
/**
* abx500_chargalg_state_to() - Change charge state
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_state_to() - Change charge state
* @di: pointer to the ab8500_chargalg structure
*
* This function gets called when a charge state change should occur
*/
static void abx500_chargalg_state_to(struct abx500_chargalg *di,
enum abx500_chargalg_states state)
static void ab8500_chargalg_state_to(struct ab8500_chargalg *di,
enum ab8500_chargalg_states state)
{
dev_dbg(di->dev,
"State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
......@@ -343,7 +343,7 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
di->charge_state = state;
}
static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di)
{
switch (di->charge_state) {
case STATE_NORMAL:
......@@ -368,13 +368,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_check_charger_connection() - Check charger connection change
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_check_charger_connection() - Check charger connection change
* @di: pointer to the ab8500_chargalg structure
*
* This function will check if there is a change in the charger connection
* and change charge state accordingly. AC has precedence over USB.
*/
static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
static int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di)
{
if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
di->susp_status.suspended_change) {
......@@ -387,23 +387,23 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
dev_dbg(di->dev, "Charging source is AC\n");
if (di->chg_info.charger_type != AC_CHG) {
di->chg_info.charger_type = AC_CHG;
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
}
} else if ((di->chg_info.conn_chg & USB_CHG) &&
!di->susp_status.usb_suspended) {
dev_dbg(di->dev, "Charging source is USB\n");
di->chg_info.charger_type = USB_CHG;
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
} else if (di->chg_info.conn_chg &&
(di->susp_status.ac_suspended ||
di->susp_status.usb_suspended)) {
dev_dbg(di->dev, "Charging is suspended\n");
di->chg_info.charger_type = NO_CHG;
abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
ab8500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
} else {
dev_dbg(di->dev, "Charging source is OFF\n");
di->chg_info.charger_type = NO_CHG;
abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
}
di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
di->susp_status.suspended_change = false;
......@@ -412,29 +412,29 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_check_current_step_status() - Check charging current
* ab8500_chargalg_check_current_step_status() - Check charging current
* step status.
* @di: pointer to the abx500_chargalg structure
* @di: pointer to the ab8500_chargalg structure
*
* This function will check if there is a change in the charging current step
* and change charge state accordingly.
*/
static void abx500_chargalg_check_current_step_status
(struct abx500_chargalg *di)
static void ab8500_chargalg_check_current_step_status
(struct ab8500_chargalg *di)
{
if (di->curr_status.curr_step_change)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
di->curr_status.curr_step_change = false;
}
/**
* abx500_chargalg_start_safety_timer() - Start charging safety timer
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_start_safety_timer() - Start charging safety timer
* @di: pointer to the ab8500_chargalg structure
*
* The safety timer is used to avoid overcharging of old or bad batteries.
* There are different timers for AC and USB
*/
static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
static void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di)
{
/* Charger-dependent expiration time in hours*/
int timer_expiration = 0;
......@@ -461,27 +461,27 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_stop_safety_timer() - Stop charging safety timer
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_stop_safety_timer() - Stop charging safety timer
* @di: pointer to the ab8500_chargalg structure
*
* The safety timer is stopped whenever the NORMAL state is exited
*/
static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
{
if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
di->events.safety_timer_expired = false;
}
/**
* abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer
* @di: pointer to the ab8500_chargalg structure
* @duration: duration of ther maintenance timer in hours
*
* The maintenance timer is used to maintain the charge in the battery once
* the battery is considered full. These timers are chosen to match the
* discharge curve of the battery
*/
static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di,
int duration)
{
hrtimer_set_expires_range(&di->maintenance_timer,
......@@ -492,26 +492,26 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
}
/**
* abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer
* @di: pointer to the ab8500_chargalg structure
*
* The maintenance timer is stopped whenever maintenance ends or when another
* state is entered
*/
static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
static void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di)
{
if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
di->events.maintenance_timer_expired = false;
}
/**
* abx500_chargalg_kick_watchdog() - Kick charger watchdog
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_kick_watchdog() - Kick charger watchdog
* @di: pointer to the ab8500_chargalg structure
*
* The charger watchdog have to be kicked periodically whenever the charger is
* on, else the ABB will reset the system
*/
static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di)
{
/* Check if charger exists and kick watchdog if charging */
if (di->ac_chg && di->ac_chg->ops.kick_wd &&
......@@ -526,8 +526,7 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
di->usb_chg->ops.kick_wd(di->usb_chg);
return di->ac_chg->ops.kick_wd(di->ac_chg);
}
else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
} else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
di->chg_info.online_chg & USB_CHG)
return di->usb_chg->ops.kick_wd(di->usb_chg);
......@@ -535,8 +534,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_ac_en() - Turn on/off the AC charger
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_ac_en() - Turn on/off the AC charger
* @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
......@@ -544,10 +543,10 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
* The AC charger will be turned on/off with the requested charge voltage and
* current
*/
static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
{
static int abx500_chargalg_ex_ac_enable_toggle;
static int ab8500_chargalg_ex_ac_enable_toggle;
if (!di->ac_chg || !di->ac_chg->ops.enable)
return -ENXIO;
......@@ -563,18 +562,18 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
/* Enable external charger */
if (enable && di->ac_chg->external &&
!abx500_chargalg_ex_ac_enable_toggle) {
!ab8500_chargalg_ex_ac_enable_toggle) {
blocking_notifier_call_chain(&charger_notifier_list,
0, di->dev);
abx500_chargalg_ex_ac_enable_toggle++;
ab8500_chargalg_ex_ac_enable_toggle++;
}
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
}
/**
* abx500_chargalg_usb_en() - Turn on/off the USB charger
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_usb_en() - Turn on/off the USB charger
* @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
......@@ -582,7 +581,7 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
* The USB charger will be turned on/off with the requested charge voltage and
* current
*/
static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
{
if (!di->usb_chg || !di->usb_chg->ops.enable)
......@@ -601,14 +600,14 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
}
/**
* abx500_chargalg_update_chg_curr() - Update charger current
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_update_chg_curr() - Update charger current
* @di: pointer to the ab8500_chargalg structure
* @iset: requested charger output current
*
* The charger output current will be updated for the charger
* that is currently in use
*/
static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
int iset)
{
/* Check if charger exists and update current if charging */
......@@ -642,19 +641,19 @@ static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
}
/**
* abx500_chargalg_stop_charging() - Stop charging
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_stop_charging() - Stop charging
* @di: pointer to the ab8500_chargalg structure
*
* This function is called from any state where charging should be stopped.
* All charging is disabled and all status parameters and timers are changed
* accordingly
*/
static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
static void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di)
{
abx500_chargalg_ac_en(di, false, 0, 0);
abx500_chargalg_usb_en(di, false, 0, 0);
abx500_chargalg_stop_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_ac_en(di, false, 0, 0);
ab8500_chargalg_usb_en(di, false, 0, 0);
ab8500_chargalg_stop_safety_timer(di);
ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
di->maintenance_chg = false;
cancel_delayed_work(&di->chargalg_wd_work);
......@@ -662,19 +661,19 @@ static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_hold_charging() - Pauses charging
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_hold_charging() - Pauses charging
* @di: pointer to the ab8500_chargalg structure
*
* This function is called in the case where maintenance charging has been
* disabled and instead a battery voltage mode is entered to check when the
* battery voltage has reached a certain recharge voltage
*/
static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di)
{
abx500_chargalg_ac_en(di, false, 0, 0);
abx500_chargalg_usb_en(di, false, 0, 0);
abx500_chargalg_stop_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_ac_en(di, false, 0, 0);
ab8500_chargalg_usb_en(di, false, 0, 0);
ab8500_chargalg_stop_safety_timer(di);
ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
di->maintenance_chg = false;
cancel_delayed_work(&di->chargalg_wd_work);
......@@ -682,30 +681,30 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_start_charging() - Start the charger
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_start_charging() - Start the charger
* @di: pointer to the ab8500_chargalg structure
* @vset: requested charger output voltage
* @iset: requested charger output current
*
* A charger will be enabled depending on the requested charger type that was
* detected previously.
*/
static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di,
int vset, int iset)
{
switch (di->chg_info.charger_type) {
case AC_CHG:
dev_dbg(di->dev,
"AC parameters: Vset %d, Ich %d\n", vset, iset);
abx500_chargalg_usb_en(di, false, 0, 0);
abx500_chargalg_ac_en(di, true, vset, iset);
ab8500_chargalg_usb_en(di, false, 0, 0);
ab8500_chargalg_ac_en(di, true, vset, iset);
break;
case USB_CHG:
dev_dbg(di->dev,
"USB parameters: Vset %d, Ich %d\n", vset, iset);
abx500_chargalg_ac_en(di, false, 0, 0);
abx500_chargalg_usb_en(di, true, vset, iset);
ab8500_chargalg_ac_en(di, false, 0, 0);
ab8500_chargalg_usb_en(di, true, vset, iset);
break;
default:
......@@ -715,13 +714,13 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
}
/**
* abx500_chargalg_check_temp() - Check battery temperature ranges
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_check_temp() - Check battery temperature ranges
* @di: pointer to the ab8500_chargalg structure
*
* The battery temperature is checked against the predefined limits and the
* charge state is changed accordingly
*/
static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di)
{
if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
......@@ -750,8 +749,8 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
di->t_hyst_norm = 0;
di->t_hyst_lowhigh = di->bm->temp_hysteresis;
} else {
/* Within hysteresis */
dev_dbg(di->dev, "Within hysteresis limit temp: %d "
/* Within hysteresis */
dev_dbg(di->dev, "Within hysteresis limit temp: %d "
"hyst_lowhigh %d, hyst normal %d\n",
di->batt_data.temp, di->t_hyst_lowhigh,
di->t_hyst_norm);
......@@ -760,12 +759,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_check_charger_voltage() - Check charger voltage
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_check_charger_voltage() - Check charger voltage
* @di: pointer to the ab8500_chargalg structure
*
* Charger voltage is checked against maximum limit
*/
static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di)
{
if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
di->chg_info.usb_chg_ok = false;
......@@ -780,14 +779,14 @@ static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
* @di: pointer to the ab8500_chargalg structure
*
* End-of-charge criteria is fulfilled when the battery voltage is above a
* certain limit and the battery current is below a certain limit for a
* predefined number of consecutive seconds. If true, the battery is full
*/
static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di)
{
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
di->charge_state == STATE_NORMAL &&
......@@ -815,7 +814,7 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
}
}
static void init_maxim_chg_curr(struct abx500_chargalg *di)
static void init_maxim_chg_curr(struct ab8500_chargalg *di)
{
di->ccm.original_iset =
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
......@@ -828,15 +827,15 @@ static void init_maxim_chg_curr(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_chg_curr_maxim - increases the charger current to
* ab8500_chargalg_chg_curr_maxim - increases the charger current to
* compensate for the system load
* @di pointer to the abx500_chargalg structure
* @di pointer to the ab8500_chargalg structure
*
* This maximization function is used to raise the charger current to get the
* battery current as close to the optimal value as possible. The battery
* current during charging is affected by the system load
*/
static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
{
int delta_i;
......@@ -867,7 +866,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
di->ccm.wait_cnt = 0;
if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
if (di->batt_data.inst_curr > di->ccm.original_iset) {
dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
" (limit %dmA) (current iset: %dmA)!\n",
di->batt_data.inst_curr, di->ccm.original_iset,
......@@ -908,21 +907,21 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
}
}
static void handle_maxim_chg_curr(struct abx500_chargalg *di)
static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
{
enum maxim_ret ret;
int result;
ret = abx500_chargalg_chg_curr_maxim(di);
ret = ab8500_chargalg_chg_curr_maxim(di);
switch (ret) {
case MAXIM_RET_CHANGE:
result = abx500_chargalg_update_chg_curr(di,
result = ab8500_chargalg_update_chg_curr(di,
di->ccm.current_iset);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
break;
case MAXIM_RET_IBAT_TOO_HIGH:
result = abx500_chargalg_update_chg_curr(di,
result = ab8500_chargalg_update_chg_curr(di,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
......@@ -935,12 +934,12 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
}
}
static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct abx500_chargalg *di;
struct ab8500_chargalg *di;
union power_supply_propval ret;
int j;
bool capacity_updated = false;
......@@ -1260,7 +1259,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
}
/**
* abx500_chargalg_external_power_changed() - callback for power supply changes
* ab8500_chargalg_external_power_changed() - callback for power supply changes
* @psy: pointer to the structure power_supply
*
* This function is the entry point of the pointer external_power_changed
......@@ -1268,26 +1267,27 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
* This function gets executed when there is a change in any external power
* supply that this driver needs to be notified of.
*/
static void abx500_chargalg_external_power_changed(struct power_supply *psy)
static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
{
struct abx500_chargalg *di = power_supply_get_drvdata(psy);
struct ab8500_chargalg *di = power_supply_get_drvdata(psy);
/*
* Trigger execution of the algorithm instantly and read
* all power_supply properties there instead
*/
queue_work(di->chargalg_wq, &di->chargalg_work);
if (di->chargalg_wq)
queue_work(di->chargalg_wq, &di->chargalg_work);
}
/**
* abx500_chargalg_algorithm() - Main function for the algorithm
* @di: pointer to the abx500_chargalg structure
* ab8500_chargalg_algorithm() - Main function for the algorithm
* @di: pointer to the ab8500_chargalg structure
*
* This is the main control function for the charging algorithm.
* It is called periodically or when something happens that will
* trigger a state change
*/
static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
{
int charger_status;
int ret;
......@@ -1295,17 +1295,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Collect data from all power_supply class devices */
class_for_each_device(power_supply_class, NULL,
di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
di->chargalg_psy, ab8500_chargalg_get_ext_psy_data);
abx500_chargalg_end_of_charge(di);
abx500_chargalg_check_temp(di);
abx500_chargalg_check_charger_voltage(di);
ab8500_chargalg_end_of_charge(di);
ab8500_chargalg_check_temp(di);
ab8500_chargalg_check_charger_voltage(di);
charger_status = abx500_chargalg_check_charger_connection(di);
abx500_chargalg_check_current_step_status(di);
charger_status = ab8500_chargalg_check_charger_connection(di);
ab8500_chargalg_check_current_step_status(di);
if (is_ab8500(di->parent)) {
ret = abx500_chargalg_check_charger_enable(di);
ret = ab8500_chargalg_check_charger_enable(di);
if (ret < 0)
dev_err(di->dev, "Checking charger is enabled error"
": Returned Value %d\n", ret);
......@@ -1320,7 +1320,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
(di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
if (di->charge_state != STATE_HANDHELD) {
di->events.safety_timer_expired = false;
abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
}
}
......@@ -1333,7 +1333,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Safety timer expiration */
else if (di->events.safety_timer_expired) {
if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
abx500_chargalg_state_to(di,
ab8500_chargalg_state_to(di,
STATE_SAFETY_TIMER_EXPIRED_INIT);
}
/*
......@@ -1344,7 +1344,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Battery removed */
else if (di->events.batt_rem) {
if (di->charge_state != STATE_BATT_REMOVED)
abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
}
/* Main or USB charger not ok. */
else if (di->events.mainextchnotok || di->events.usbchargernotok) {
......@@ -1354,7 +1354,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
*/
if (di->charge_state != STATE_CHG_NOT_OK &&
!di->events.vbus_collapsed)
abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
}
/* VBUS, Main or VBAT OVV. */
else if (di->events.vbus_ovv ||
......@@ -1363,31 +1363,31 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
!di->chg_info.usb_chg_ok ||
!di->chg_info.ac_chg_ok) {
if (di->charge_state != STATE_OVV_PROTECT)
abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
}
/* USB Thermal, stop charging */
else if (di->events.main_thermal_prot ||
di->events.usb_thermal_prot) {
if (di->charge_state != STATE_HW_TEMP_PROTECT)
abx500_chargalg_state_to(di,
ab8500_chargalg_state_to(di,
STATE_HW_TEMP_PROTECT_INIT);
}
/* Battery temp over/under */
else if (di->events.btemp_underover) {
if (di->charge_state != STATE_TEMP_UNDEROVER)
abx500_chargalg_state_to(di,
ab8500_chargalg_state_to(di,
STATE_TEMP_UNDEROVER_INIT);
}
/* Watchdog expired */
else if (di->events.ac_wd_expired ||
di->events.usb_wd_expired) {
if (di->charge_state != STATE_WD_EXPIRED)
abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
}
/* Battery temp high/low */
else if (di->events.btemp_lowhigh) {
if (di->charge_state != STATE_TEMP_LOWHIGH)
abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
}
dev_dbg(di->dev,
......@@ -1419,9 +1419,9 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
switch (di->charge_state) {
case STATE_HANDHELD_INIT:
abx500_chargalg_stop_charging(di);
ab8500_chargalg_stop_charging(di);
di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
abx500_chargalg_state_to(di, STATE_HANDHELD);
ab8500_chargalg_state_to(di, STATE_HANDHELD);
fallthrough;
case STATE_HANDHELD:
......@@ -1429,14 +1429,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_SUSPENDED_INIT:
if (di->susp_status.ac_suspended)
abx500_chargalg_ac_en(di, false, 0, 0);
ab8500_chargalg_ac_en(di, false, 0, 0);
if (di->susp_status.usb_suspended)
abx500_chargalg_usb_en(di, false, 0, 0);
abx500_chargalg_stop_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_usb_en(di, false, 0, 0);
ab8500_chargalg_stop_safety_timer(di);
ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
di->maintenance_chg = false;
abx500_chargalg_state_to(di, STATE_SUSPENDED);
ab8500_chargalg_state_to(di, STATE_SUSPENDED);
power_supply_changed(di->chargalg_psy);
fallthrough;
......@@ -1445,29 +1445,29 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
break;
case STATE_BATT_REMOVED_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_BATT_REMOVED);
fallthrough;
case STATE_BATT_REMOVED:
if (!di->events.batt_rem)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_HW_TEMP_PROTECT_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
fallthrough;
case STATE_HW_TEMP_PROTECT:
if (!di->events.main_thermal_prot &&
!di->events.usb_thermal_prot)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_OVV_PROTECT_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_OVV_PROTECT);
fallthrough;
case STATE_OVV_PROTECT:
......@@ -1476,23 +1476,23 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
!di->events.batt_ovv &&
di->chg_info.usb_chg_ok &&
di->chg_info.ac_chg_ok)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_CHG_NOT_OK_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK);
fallthrough;
case STATE_CHG_NOT_OK:
if (!di->events.mainextchnotok &&
!di->events.usbchargernotok)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_SAFETY_TIMER_EXPIRED_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
fallthrough;
case STATE_SAFETY_TIMER_EXPIRED:
......@@ -1501,20 +1501,20 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_NORMAL_INIT:
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
abx500_chargalg_stop_charging(di);
ab8500_chargalg_stop_charging(di);
else {
curr_step_lvl = di->bm->bat_type[
di->bm->batt_id].normal_cur_lvl
* di->curr_status.curr_step
/ CHARGALG_CURR_STEP_HIGH;
abx500_chargalg_start_charging(di,
ab8500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id]
.normal_vol_lvl, curr_step_lvl);
}
abx500_chargalg_state_to(di, STATE_NORMAL);
abx500_chargalg_start_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_state_to(di, STATE_NORMAL);
ab8500_chargalg_start_safety_timer(di);
ab8500_chargalg_stop_maintenance_timer(di);
init_maxim_chg_curr(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
di->eoc_cnt = 0;
......@@ -1528,104 +1528,103 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
di->maintenance_chg) {
if (di->bm->no_maintenance)
abx500_chargalg_state_to(di,
ab8500_chargalg_state_to(di,
STATE_WAIT_FOR_RECHARGE_INIT);
else
abx500_chargalg_state_to(di,
ab8500_chargalg_state_to(di,
STATE_MAINTENANCE_A_INIT);
}
break;
/* This state will be used when the maintenance state is disabled */
case STATE_WAIT_FOR_RECHARGE_INIT:
abx500_chargalg_hold_charging(di);
abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
ab8500_chargalg_hold_charging(di);
ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
fallthrough;
case STATE_WAIT_FOR_RECHARGE:
if (di->batt_data.percent <=
di->bm->bat_type[di->bm->batt_id].
recharge_cap)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
di->bm->bat_type[di->bm->batt_id].recharge_cap)
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_MAINTENANCE_A_INIT:
abx500_chargalg_stop_safety_timer(di);
abx500_chargalg_start_maintenance_timer(di,
ab8500_chargalg_stop_safety_timer(di);
ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_chg_timer_h);
abx500_chargalg_start_charging(di,
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_a_cur_lvl);
abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_MAINTENANCE_A:
if (di->events.maintenance_timer_expired) {
abx500_chargalg_stop_maintenance_timer(di);
abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
ab8500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
}
break;
case STATE_MAINTENANCE_B_INIT:
abx500_chargalg_start_maintenance_timer(di,
ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_chg_timer_h);
abx500_chargalg_start_charging(di,
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_b_cur_lvl);
abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_MAINTENANCE_B:
if (di->events.maintenance_timer_expired) {
abx500_chargalg_stop_maintenance_timer(di);
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
}
break;
case STATE_TEMP_LOWHIGH_INIT:
abx500_chargalg_start_charging(di,
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].low_high_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].low_high_cur_lvl);
abx500_chargalg_stop_maintenance_timer(di);
ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_TEMP_LOWHIGH:
if (!di->events.btemp_lowhigh)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_WD_EXPIRED_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_WD_EXPIRED);
fallthrough;
case STATE_WD_EXPIRED:
if (!di->events.ac_wd_expired &&
!di->events.usb_wd_expired)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_TEMP_UNDEROVER_INIT:
abx500_chargalg_stop_charging(di);
abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
ab8500_chargalg_stop_charging(di);
ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
fallthrough;
case STATE_TEMP_UNDEROVER:
if (!di->events.btemp_underover)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
}
......@@ -1637,17 +1636,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
}
/**
* abx500_chargalg_periodic_work() - Periodic work for the algorithm
* ab8500_chargalg_periodic_work() - Periodic work for the algorithm
* @work: pointer to the work_struct structure
*
* Work queue function for the charging algorithm
*/
static void abx500_chargalg_periodic_work(struct work_struct *work)
static void ab8500_chargalg_periodic_work(struct work_struct *work)
{
struct abx500_chargalg *di = container_of(work,
struct abx500_chargalg, chargalg_periodic_work.work);
struct ab8500_chargalg *di = container_of(work,
struct ab8500_chargalg, chargalg_periodic_work.work);
abx500_chargalg_algorithm(di);
ab8500_chargalg_algorithm(di);
/*
* If a charger is connected then the battery has to be monitored
......@@ -1664,20 +1663,18 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
}
/**
* abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
* ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog
* @work: pointer to the work_struct structure
*
* Work queue function for kicking the charger watchdog
*/
static void abx500_chargalg_wd_work(struct work_struct *work)
static void ab8500_chargalg_wd_work(struct work_struct *work)
{
int ret;
struct abx500_chargalg *di = container_of(work,
struct abx500_chargalg, chargalg_wd_work.work);
dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
struct ab8500_chargalg *di = container_of(work,
struct ab8500_chargalg, chargalg_wd_work.work);
ret = abx500_chargalg_kick_watchdog(di);
ret = ab8500_chargalg_kick_watchdog(di);
if (ret < 0)
dev_err(di->dev, "failed to kick watchdog\n");
......@@ -1686,21 +1683,21 @@ static void abx500_chargalg_wd_work(struct work_struct *work)
}
/**
* abx500_chargalg_work() - Work to run the charging algorithm instantly
* ab8500_chargalg_work() - Work to run the charging algorithm instantly
* @work: pointer to the work_struct structure
*
* Work queue function for calling the charging algorithm
*/
static void abx500_chargalg_work(struct work_struct *work)
static void ab8500_chargalg_work(struct work_struct *work)
{
struct abx500_chargalg *di = container_of(work,
struct abx500_chargalg, chargalg_work);
struct ab8500_chargalg *di = container_of(work,
struct ab8500_chargalg, chargalg_work);
abx500_chargalg_algorithm(di);
ab8500_chargalg_algorithm(di);
}
/**
* abx500_chargalg_get_property() - get the chargalg properties
* ab8500_chargalg_get_property() - get the chargalg properties
* @psy: pointer to the power_supply structure
* @psp: pointer to the power_supply_property structure
* @val: pointer to the power_supply_propval union
......@@ -1711,11 +1708,11 @@ static void abx500_chargalg_work(struct work_struct *work)
* health: health of the battery
* Returns error code in case of failure else 0 on success
*/
static int abx500_chargalg_get_property(struct power_supply *psy,
static int ab8500_chargalg_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct abx500_chargalg *di = power_supply_get_drvdata(psy);
struct ab8500_chargalg *di = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
......@@ -1744,16 +1741,16 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
/* Exposure to the sysfs interface */
static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n", di->curr_status.curr_step);
}
static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di,
const char *buf, size_t length)
{
long int param;
long param;
int ret;
ret = kstrtol(buf, 10, &param);
......@@ -1775,7 +1772,7 @@ static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
}
static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
static ssize_t ab8500_chargalg_en_show(struct ab8500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n",
......@@ -1783,10 +1780,10 @@ static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
di->susp_status.usb_suspended);
}
static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
static ssize_t ab8500_chargalg_en_store(struct ab8500_chargalg *di,
const char *buf, size_t length)
{
long int param;
long param;
int ac_usb;
int ret;
......@@ -1830,22 +1827,22 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
return strlen(buf);
}
static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
__ATTR(chargalg, 0644, abx500_chargalg_en_show,
abx500_chargalg_en_store);
static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_en_charger =
__ATTR(chargalg, 0644, ab8500_chargalg_en_show,
ab8500_chargalg_en_store);
static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
__ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
abx500_chargalg_curr_step_store);
static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_curr_step =
__ATTR(chargalg_curr_step, 0644, ab8500_chargalg_curr_step_show,
ab8500_chargalg_curr_step_store);
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
struct abx500_chargalg_sysfs_entry, attr);
struct ab8500_chargalg_sysfs_entry *entry = container_of(attr,
struct ab8500_chargalg_sysfs_entry, attr);
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
struct ab8500_chargalg *di = container_of(kobj,
struct ab8500_chargalg, chargalg_kobject);
if (!entry->show)
return -EIO;
......@@ -1853,14 +1850,14 @@ static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
return entry->show(di, buf);
}
static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
static ssize_t ab8500_chargalg_sysfs_charger(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t length)
{
struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
struct abx500_chargalg_sysfs_entry, attr);
struct ab8500_chargalg_sysfs_entry *entry = container_of(attr,
struct ab8500_chargalg_sysfs_entry, attr);
struct abx500_chargalg *di = container_of(kobj,
struct abx500_chargalg, chargalg_kobject);
struct ab8500_chargalg *di = container_of(kobj,
struct ab8500_chargalg, chargalg_kobject);
if (!entry->store)
return -EIO;
......@@ -1868,47 +1865,47 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
return entry->store(di, buf, length);
}
static struct attribute *abx500_chargalg_chg[] = {
&abx500_chargalg_en_charger.attr,
&abx500_chargalg_curr_step.attr,
static struct attribute *ab8500_chargalg_chg[] = {
&ab8500_chargalg_en_charger.attr,
&ab8500_chargalg_curr_step.attr,
NULL,
};
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
.show = abx500_chargalg_sysfs_show,
.store = abx500_chargalg_sysfs_charger,
static const struct sysfs_ops ab8500_chargalg_sysfs_ops = {
.show = ab8500_chargalg_sysfs_show,
.store = ab8500_chargalg_sysfs_charger,
};
static struct kobj_type abx500_chargalg_ktype = {
.sysfs_ops = &abx500_chargalg_sysfs_ops,
.default_attrs = abx500_chargalg_chg,
static struct kobj_type ab8500_chargalg_ktype = {
.sysfs_ops = &ab8500_chargalg_sysfs_ops,
.default_attrs = ab8500_chargalg_chg,
};
/**
* abx500_chargalg_sysfs_exit() - de-init of sysfs entry
* @di: pointer to the struct abx500_chargalg
* ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
* @di: pointer to the struct ab8500_chargalg
*
* This function removes the entry in sysfs.
*/
static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
static void ab8500_chargalg_sysfs_exit(struct ab8500_chargalg *di)
{
kobject_del(&di->chargalg_kobject);
}
/**
* abx500_chargalg_sysfs_init() - init of sysfs entry
* @di: pointer to the struct abx500_chargalg
* ab8500_chargalg_sysfs_init() - init of sysfs entry
* @di: pointer to the struct ab8500_chargalg
*
* This function adds an entry in sysfs.
* Returns error code in case of failure else 0(on success)
*/
static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
static int ab8500_chargalg_sysfs_init(struct ab8500_chargalg *di)
{
int ret = 0;
ret = kobject_init_and_add(&di->chargalg_kobject,
&abx500_chargalg_ktype,
NULL, "abx500_chargalg");
&ab8500_chargalg_ktype,
NULL, "ab8500_chargalg");
if (ret < 0)
dev_err(di->dev, "failed to create sysfs entry\n");
......@@ -1916,9 +1913,9 @@ static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
}
/* Exposure to the sysfs interface <<END>> */
static int __maybe_unused abx500_chargalg_resume(struct device *dev)
static int __maybe_unused ab8500_chargalg_resume(struct device *dev)
{
struct abx500_chargalg *di = dev_get_drvdata(dev);
struct ab8500_chargalg *di = dev_get_drvdata(dev);
/* Kick charger watchdog if charging (any charger online) */
if (di->chg_info.online_chg)
......@@ -1933,9 +1930,9 @@ static int __maybe_unused abx500_chargalg_resume(struct device *dev)
return 0;
}
static int __maybe_unused abx500_chargalg_suspend(struct device *dev)
static int __maybe_unused ab8500_chargalg_suspend(struct device *dev)
{
struct abx500_chargalg *di = dev_get_drvdata(dev);
struct ab8500_chargalg *di = dev_get_drvdata(dev);
if (di->chg_info.online_chg)
cancel_delayed_work_sync(&di->chargalg_wd_work);
......@@ -1949,22 +1946,22 @@ static char *supply_interface[] = {
"ab8500_fg",
};
static const struct power_supply_desc abx500_chargalg_desc = {
.name = "abx500_chargalg",
static const struct power_supply_desc ab8500_chargalg_desc = {
.name = "ab8500_chargalg",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = abx500_chargalg_props,
.num_properties = ARRAY_SIZE(abx500_chargalg_props),
.get_property = abx500_chargalg_get_property,
.external_power_changed = abx500_chargalg_external_power_changed,
.properties = ab8500_chargalg_props,
.num_properties = ARRAY_SIZE(ab8500_chargalg_props),
.get_property = ab8500_chargalg_get_property,
.external_power_changed = ab8500_chargalg_external_power_changed,
};
static int abx500_chargalg_bind(struct device *dev, struct device *master,
static int ab8500_chargalg_bind(struct device *dev, struct device *master,
void *data)
{
struct abx500_chargalg *di = dev_get_drvdata(dev);
struct ab8500_chargalg *di = dev_get_drvdata(dev);
/* Create a work queue for the chargalg */
di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq",
di->chargalg_wq = alloc_ordered_workqueue("ab8500_chargalg_wq",
WQ_MEM_RECLAIM);
if (di->chargalg_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n");
......@@ -1977,10 +1974,10 @@ static int abx500_chargalg_bind(struct device *dev, struct device *master,
return 0;
}
static void abx500_chargalg_unbind(struct device *dev, struct device *master,
static void ab8500_chargalg_unbind(struct device *dev, struct device *master,
void *data)
{
struct abx500_chargalg *di = dev_get_drvdata(dev);
struct ab8500_chargalg *di = dev_get_drvdata(dev);
/* Stop all timers and work */
hrtimer_cancel(&di->safety_timer);
......@@ -1995,16 +1992,16 @@ static void abx500_chargalg_unbind(struct device *dev, struct device *master,
flush_scheduled_work();
}
static const struct component_ops abx500_chargalg_component_ops = {
.bind = abx500_chargalg_bind,
.unbind = abx500_chargalg_unbind,
static const struct component_ops ab8500_chargalg_component_ops = {
.bind = ab8500_chargalg_bind,
.unbind = ab8500_chargalg_unbind,
};
static int abx500_chargalg_probe(struct platform_device *pdev)
static int ab8500_chargalg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct power_supply_config psy_cfg = {};
struct abx500_chargalg *di;
struct ab8500_chargalg *di;
int ret = 0;
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
......@@ -2023,28 +2020,28 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
/* Initilialize safety timer */
hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
di->safety_timer.function = abx500_chargalg_safety_timer_expired;
di->safety_timer.function = ab8500_chargalg_safety_timer_expired;
/* Initilialize maintenance timer */
hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
di->maintenance_timer.function =
abx500_chargalg_maintenance_timer_expired;
ab8500_chargalg_maintenance_timer_expired;
/* Init work for chargalg */
INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work,
abx500_chargalg_periodic_work);
ab8500_chargalg_periodic_work);
INIT_DEFERRABLE_WORK(&di->chargalg_wd_work,
abx500_chargalg_wd_work);
ab8500_chargalg_wd_work);
/* Init work for chargalg */
INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
INIT_WORK(&di->chargalg_work, ab8500_chargalg_work);
/* To detect charger at startup */
di->chg_info.prev_conn_chg = -1;
/* Register chargalg power supply class */
di->chargalg_psy = devm_power_supply_register(di->dev,
&abx500_chargalg_desc,
&ab8500_chargalg_desc,
&psy_cfg);
if (IS_ERR(di->chargalg_psy)) {
dev_err(di->dev, "failed to register chargalg psy\n");
......@@ -2054,7 +2051,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, di);
/* sysfs interface to enable/disable charging from user space */
ret = abx500_chargalg_sysfs_init(di);
ret = ab8500_chargalg_sysfs_init(di);
if (ret) {
dev_err(di->dev, "failed to create sysfs entry\n");
return ret;
......@@ -2062,38 +2059,38 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
dev_info(di->dev, "probe success\n");
return component_add(dev, &abx500_chargalg_component_ops);
return component_add(dev, &ab8500_chargalg_component_ops);
}
static int abx500_chargalg_remove(struct platform_device *pdev)
static int ab8500_chargalg_remove(struct platform_device *pdev)
{
struct abx500_chargalg *di = platform_get_drvdata(pdev);
struct ab8500_chargalg *di = platform_get_drvdata(pdev);
component_del(&pdev->dev, &abx500_chargalg_component_ops);
component_del(&pdev->dev, &ab8500_chargalg_component_ops);
/* sysfs interface to enable/disable charging from user space */
abx500_chargalg_sysfs_exit(di);
ab8500_chargalg_sysfs_exit(di);
return 0;
}
static SIMPLE_DEV_PM_OPS(abx500_chargalg_pm_ops, abx500_chargalg_suspend, abx500_chargalg_resume);
static SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume);
static const struct of_device_id ab8500_chargalg_match[] = {
{ .compatible = "stericsson,ab8500-chargalg", },
{ },
};
struct platform_driver abx500_chargalg_driver = {
.probe = abx500_chargalg_probe,
.remove = abx500_chargalg_remove,
struct platform_driver ab8500_chargalg_driver = {
.probe = ab8500_chargalg_probe,
.remove = ab8500_chargalg_remove,
.driver = {
.name = "ab8500-chargalg",
.name = "ab8500_chargalg",
.of_match_table = ab8500_chargalg_match,
.pm = &abx500_chargalg_pm_ops,
.pm = &ab8500_chargalg_pm_ops,
},
};
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
MODULE_ALIAS("platform:abx500-chargalg");
MODULE_DESCRIPTION("abx500 battery charging algorithm");
MODULE_ALIAS("platform:ab8500-chargalg");
MODULE_DESCRIPTION("ab8500 battery charging algorithm");
......@@ -292,7 +292,7 @@ struct ab8500_charger {
struct iio_channel *adc_main_charger_c;
struct iio_channel *adc_vbus_v;
struct iio_channel *adc_usb_charger_c;
struct abx500_bm_data *bm;
struct ab8500_bm_data *bm;
struct ab8500_charger_event_flags flags;
struct ab8500_charger_usb_state usb_state;
struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
......@@ -3388,7 +3388,7 @@ static const struct component_master_ops ab8500_charger_comp_ops = {
static struct platform_driver *const ab8500_charger_component_drivers[] = {
&ab8500_fg_driver,
&ab8500_btemp_driver,
&abx500_chargalg_driver,
&ab8500_chargalg_driver,
};
static int ab8500_charger_compare_dev(struct device *dev, void *data)
......
......@@ -34,6 +34,7 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/iio/consumer.h>
#include <linux/kernel.h>
#include <linux/fixp-arith.h>
#include "ab8500-bm.h"
......@@ -56,9 +57,6 @@
/* FG constants */
#define BATT_OVV 0x01
#define interpolate(x, x1, y1, x2, y2) \
((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
/**
* struct ab8500_fg_interrupts - ab8500 fg interrupts
* @name: name of the interrupt
......@@ -227,7 +225,7 @@ struct ab8500_fg {
struct ab8500_fg_avg_cap avg_cap;
struct ab8500 *parent;
struct iio_channel *main_bat_v;
struct abx500_bm_data *bm;
struct ab8500_bm_data *bm;
struct power_supply *fg_psy;
struct workqueue_struct *fg_wq;
struct delayed_work fg_periodic_work;
......@@ -856,7 +854,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
{
int i, tbl_size;
const struct abx500_v_to_cap *tbl;
const struct ab8500_v_to_cap *tbl;
int cap = 0;
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl;
......@@ -868,11 +866,12 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
}
if ((i > 0) && (i < tbl_size)) {
cap = interpolate(voltage,
cap = fixp_linear_interpolate(
tbl[i].voltage,
tbl[i].capacity * 10,
tbl[i-1].voltage,
tbl[i-1].capacity * 10);
tbl[i-1].capacity * 10,
voltage);
} else if (i == 0) {
cap = 1000;
} else {
......@@ -920,11 +919,12 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
}
if ((i > 0) && (i < tbl_size)) {
resist = interpolate(di->bat_temp / 10,
resist = fixp_linear_interpolate(
tbl[i].temp,
tbl[i].resist,
tbl[i-1].temp,
tbl[i-1].resist);
tbl[i-1].resist,
di->bat_temp / 10);
} else if (i == 0) {
resist = tbl[0].resist;
} else {
......@@ -2235,7 +2235,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_TYPE_BATTERY:
if (!di->flags.batt_id_received &&
di->bm->batt_id != BATTERY_UNKNOWN) {
const struct abx500_battery_type *b;
const struct ab8500_battery_type *b;
b = &(di->bm->bat_type[di->bm->batt_id]);
......
......@@ -813,7 +813,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
if (val == 0)
return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
......@@ -823,7 +823,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
if (info->cable.edev == NULL) {
dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
dev_dbg(dev, "%s is not ready, probe deferred\n",
AXP288_EXTCON_DEV_NAME);
return -EPROBE_DEFER;
}
......@@ -834,8 +834,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
return -EPROBE_DEFER;
}
dev_info(&pdev->dev,
"Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
}
platform_set_drvdata(pdev, info);
......@@ -874,7 +873,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
if (info->otg.cable) {
ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
ret = devm_extcon_register_notifier(dev, info->otg.cable,
EXTCON_USB_HOST, &info->otg.id_nb);
if (ret) {
dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
......@@ -899,7 +898,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
NULL, axp288_charger_irq_thread_handler,
IRQF_ONESHOT, info->pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
dev_err(dev, "failed to request interrupt=%d\n",
info->irq[i]);
return ret;
}
......
......@@ -2,7 +2,8 @@
/*
* axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
*
* Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2020-2021 Andrejus Basovas <xxx@yyy.tld>
* Copyright (C) 2016-2021 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2014 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -19,38 +20,37 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/iio/consumer.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/unaligned.h>
#include <asm/iosf_mbi.h>
#define PS_STAT_VBUS_TRIGGER (1 << 0)
#define PS_STAT_BAT_CHRG_DIR (1 << 2)
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
#define PS_STAT_VBUS_VALID (1 << 4)
#define PS_STAT_VBUS_PRESENT (1 << 5)
#define PS_STAT_VBUS_TRIGGER (1 << 0)
#define PS_STAT_BAT_CHRG_DIR (1 << 2)
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
#define PS_STAT_VBUS_VALID (1 << 4)
#define PS_STAT_VBUS_PRESENT (1 << 5)
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
#define CHRG_STAT_BAT_VALID (1 << 4)
#define CHRG_STAT_BAT_PRESENT (1 << 5)
#define CHRG_STAT_BAT_PRESENT (1 << 5)
#define CHRG_STAT_CHARGING (1 << 6)
#define CHRG_STAT_PMIC_OTP (1 << 7)
#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_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_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_4150MV 0x1 /* 4.15V */
#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
#define CHRG_CCCV_CHG_EN (1 << 7)
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
#define FG_CNTL_CAP_ADJ_EN (1 << 5)
#define FG_CNTL_CC_EN (1 << 6)
#define FG_CNTL_GAUGE_EN (1 << 7)
......@@ -71,23 +71,23 @@
#define FG_CC_CAP_VALID (1 << 7)
#define FG_CC_CAP_VAL_MASK 0x7F
#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */
#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */
#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */
#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */
#define NR_RETRY_CNT 3
#define DEV_NAME "axp288_fuel_gauge"
#define DEV_NAME "axp288_fuel_gauge"
/* 1.1mV per LSB expressed in uV */
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
/* properties converted to uV, uA */
#define PROP_VOLT(a) ((a) * 1000)
#define PROP_CURR(a) ((a) * 1000)
#define PROP_VOLT(a) ((a) * 1000)
#define PROP_CURR(a) ((a) * 1000)
#define AXP288_FG_INTR_NUM 6
#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
#define AXP288_FG_INTR_NUM 6
enum {
QWBTU_IRQ = 0,
WBTU_IRQ,
......@@ -98,9 +98,6 @@ enum {
};
enum {
BAT_TEMP = 0,
PMIC_TEMP,
SYSTEM_TEMP,
BAT_CHRG_CURR,
BAT_D_CURR,
BAT_VOLT,
......@@ -108,7 +105,7 @@ enum {
};
struct axp288_fg_info {
struct platform_device *pdev;
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
int irq[AXP288_FG_INTR_NUM];
......@@ -117,7 +114,21 @@ struct axp288_fg_info {
struct mutex lock;
int status;
int max_volt;
int pwr_op;
int low_cap;
struct dentry *debug_file;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int pwr_stat;
int fg_res;
int bat_volt;
int d_curr;
int c_curr;
int ocv;
int fg_cc_mtr1;
int fg_des_cap1;
};
static enum power_supply_property fuel_gauge_props[] = {
......@@ -137,17 +148,12 @@ static enum power_supply_property fuel_gauge_props[] = {
static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
{
int ret, i;
unsigned int val;
int ret;
for (i = 0; i < NR_RETRY_CNT; i++) {
ret = regmap_read(info->regmap, reg, &val);
if (ret != -EBUSY)
break;
}
ret = regmap_read(info->regmap, reg, &val);
if (ret < 0) {
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
......@@ -161,7 +167,7 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
ret = regmap_write(info->regmap, reg, (unsigned int)val);
if (ret < 0)
dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret);
return ret;
}
......@@ -173,15 +179,13 @@ static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
if (ret < 0) {
dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
reg, ret);
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
ret = get_unaligned_be16(buf);
if (!(ret & FG_15BIT_WORD_VALID)) {
dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n",
reg);
dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg);
return -ENXIO;
}
......@@ -195,8 +199,7 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
if (ret < 0) {
dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
reg, ret);
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
......@@ -204,139 +207,78 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
}
#ifdef CONFIG_DEBUG_FS
static int fuel_gauge_debug_show(struct seq_file *s, void *data)
static int fuel_gauge_update_registers(struct axp288_fg_info *info)
{
struct axp288_fg_info *info = s->private;
int raw_val, ret;
seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
AXP20X_PWR_INPUT_STATUS,
fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
AXP20X_PWR_OP_MODE,
fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
AXP20X_CHRG_CTRL1,
fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
seq_printf(s, " VLTF[%02x] : %02x\n",
AXP20X_V_LTF_DISCHRG,
fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
seq_printf(s, " VHTF[%02x] : %02x\n",
AXP20X_V_HTF_DISCHRG,
fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
seq_printf(s, " CC_CTRL[%02x] : %02x\n",
AXP20X_CC_CTRL,
fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
AXP20X_FG_RES,
fuel_gauge_reg_readb(info, AXP20X_FG_RES));
seq_printf(s, " FG_RDC1[%02x] : %02x\n",
AXP288_FG_RDC1_REG,
fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
seq_printf(s, " FG_RDC0[%02x] : %02x\n",
AXP288_FG_RDC0_REG,
fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
seq_printf(s, " FG_OCV[%02x] : %04x\n",
AXP288_FG_OCVH_REG,
fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
AXP288_FG_DES_CAP1_REG,
fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
seq_printf(s, " FG_CC_MTR[%02x] : %04x\n",
AXP288_FG_CC_MTR1_REG,
fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));
seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
AXP288_FG_OCV_CAP_REG,
fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
seq_printf(s, " FG_CC_CAP[%02x] : %02x\n",
AXP288_FG_CC_CAP_REG,
fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
AXP288_FG_LOW_CAP_REG,
fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
AXP288_FG_TUNE0,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
AXP288_FG_TUNE1,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
AXP288_FG_TUNE2,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
AXP288_FG_TUNE3,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
AXP288_FG_TUNE4,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
AXP288_FG_TUNE5,
fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-batttemp : %d\n", raw_val);
ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-systtemp : %d\n", raw_val);
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
if (ret >= 0)
seq_printf(s, "axp288-battvolt : %d\n", raw_val);
int ret;
return 0;
}
if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
return 0;
DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug);
dev_dbg(info->dev, "Fuel Gauge updating register values...\n");
static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
{
info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
info, &fuel_gauge_debug_fops);
}
ret = iosf_mbi_block_punit_i2c_access();
if (ret < 0)
return ret;
static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
{
debugfs_remove(info->debug_file);
}
#else
static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
{
}
static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
{
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
if (ret < 0)
goto out;
info->pwr_stat = ret;
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
if (ret < 0)
goto out;
info->fg_res = ret;
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt);
if (ret < 0)
goto out;
if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) {
info->d_curr = 0;
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr);
if (ret < 0)
goto out;
} else {
info->c_curr = 0;
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr);
if (ret < 0)
goto out;
}
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
if (ret < 0)
goto out;
info->ocv = ret;
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
if (ret < 0)
goto out;
info->fg_cc_mtr1 = ret;
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
if (ret < 0)
goto out;
info->fg_des_cap1 = ret;
info->last_updated = jiffies;
info->valid = 1;
ret = 0;
out:
iosf_mbi_unblock_punit_i2c_access();
return ret;
}
#endif
static void fuel_gauge_get_status(struct axp288_fg_info *info)
{
int pwr_stat, fg_res, curr, ret;
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
if (pwr_stat < 0) {
dev_err(&info->pdev->dev,
"PWR STAT read failed:%d\n", pwr_stat);
return;
}
int pwr_stat = info->pwr_stat;
int fg_res = info->fg_res;
int curr = info->d_curr;
/* Report full if Vbus is valid and the reported capacity is 100% */
if (!(pwr_stat & PS_STAT_VBUS_VALID))
goto not_full;
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
if (fg_res < 0) {
dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
return;
}
if (!(fg_res & FG_REP_CAP_VALID))
goto not_full;
......@@ -354,11 +296,6 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
goto not_full;
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
if (ret < 0) {
dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
return;
}
if (curr == 0) {
info->status = POWER_SUPPLY_STATUS_FULL;
return;
......@@ -371,61 +308,16 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
info->status = POWER_SUPPLY_STATUS_DISCHARGING;
}
static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
{
int ret = 0, raw_val;
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
if (ret < 0)
goto vbatt_read_fail;
*vbatt = VOLTAGE_FROM_ADC(raw_val);
vbatt_read_fail:
return ret;
}
static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
{
int ret, discharge;
/* First check discharge current, so that we do only 1 read on bat. */
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
if (ret < 0)
return ret;
if (discharge > 0) {
*cur = -1 * discharge;
return 0;
}
return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
}
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
{
int ret;
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
if (ret >= 0)
*vocv = VOLTAGE_FROM_ADC(ret);
return ret;
}
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
{
int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
ret = fuel_gauge_get_vocv(info, &vocv);
if (ret < 0)
goto health_read_fail;
int vocv = VOLTAGE_FROM_ADC(info->ocv);
int health = POWER_SUPPLY_HEALTH_UNKNOWN;
if (vocv > info->max_volt)
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
else
health = POWER_SUPPLY_HEALTH_GOOD;
health_read_fail:
return health;
}
......@@ -434,9 +326,14 @@ static int fuel_gauge_get_property(struct power_supply *ps,
union power_supply_propval *val)
{
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
int ret = 0, value;
int ret, value;
mutex_lock(&info->lock);
ret = fuel_gauge_update_registers(info);
if (ret < 0)
goto out;
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
fuel_gauge_get_status(info);
......@@ -446,78 +343,52 @@ static int fuel_gauge_get_property(struct power_supply *ps,
val->intval = fuel_gauge_battery_health(info);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = fuel_gauge_get_vbatt(info, &value);
if (ret < 0)
goto fuel_gauge_read_err;
value = VOLTAGE_FROM_ADC(info->bat_volt);
val->intval = PROP_VOLT(value);
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
ret = fuel_gauge_get_vocv(info, &value);
if (ret < 0)
goto fuel_gauge_read_err;
value = VOLTAGE_FROM_ADC(info->ocv);
val->intval = PROP_VOLT(value);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = fuel_gauge_get_current(info, &value);
if (ret < 0)
goto fuel_gauge_read_err;
if (info->d_curr > 0)
value = -1 * info->d_curr;
else
value = info->c_curr;
val->intval = PROP_CURR(value);
break;
case POWER_SUPPLY_PROP_PRESENT:
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
if (ret < 0)
goto fuel_gauge_read_err;
if (ret & CHRG_STAT_BAT_PRESENT)
if (info->pwr_op & CHRG_STAT_BAT_PRESENT)
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
if (ret < 0)
goto fuel_gauge_read_err;
if (!(ret & FG_REP_CAP_VALID))
dev_err(&info->pdev->dev,
"capacity measurement not valid\n");
val->intval = (ret & FG_REP_CAP_VAL_MASK);
if (!(info->fg_res & FG_REP_CAP_VALID))
dev_err(info->dev, "capacity measurement not valid\n");
val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK);
break;
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
if (ret < 0)
goto fuel_gauge_read_err;
val->intval = (ret & 0x0f);
val->intval = (info->low_cap & 0x0f);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
if (ret < 0)
goto fuel_gauge_read_err;
val->intval = ret * FG_DES_CAP_RES_LSB;
val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
if (ret < 0)
goto fuel_gauge_read_err;
val->intval = ret * FG_DES_CAP_RES_LSB;
val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = PROP_VOLT(info->max_volt);
break;
default:
mutex_unlock(&info->lock);
return -EINVAL;
ret = -EINVAL;
}
mutex_unlock(&info->lock);
return 0;
fuel_gauge_read_err:
out:
mutex_unlock(&info->lock);
return ret;
}
......@@ -527,7 +398,7 @@ static int fuel_gauge_set_property(struct power_supply *ps,
const union power_supply_propval *val)
{
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
int ret = 0;
int new_low_cap, ret = 0;
mutex_lock(&info->lock);
switch (prop) {
......@@ -536,12 +407,12 @@ static int fuel_gauge_set_property(struct power_supply *ps,
ret = -EINVAL;
break;
}
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
if (ret < 0)
break;
ret &= 0xf0;
ret |= (val->intval & 0xf);
ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
new_low_cap = info->low_cap;
new_low_cap &= 0xf0;
new_low_cap |= (val->intval & 0xf);
ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap);
if (ret == 0)
info->low_cap = new_low_cap;
break;
default:
ret = -EINVAL;
......@@ -579,37 +450,35 @@ static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
}
if (i >= AXP288_FG_INTR_NUM) {
dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
dev_warn(info->dev, "spurious interrupt!!\n");
return IRQ_NONE;
}
switch (i) {
case QWBTU_IRQ:
dev_info(&info->pdev->dev,
"Quit Battery under temperature in work mode IRQ (QWBTU)\n");
dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
break;
case WBTU_IRQ:
dev_info(&info->pdev->dev,
"Battery under temperature in work mode IRQ (WBTU)\n");
dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n");
break;
case QWBTO_IRQ:
dev_info(&info->pdev->dev,
"Quit Battery over temperature in work mode IRQ (QWBTO)\n");
dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
break;
case WBTO_IRQ:
dev_info(&info->pdev->dev,
"Battery over temperature in work mode IRQ (WBTO)\n");
dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n");
break;
case WL2_IRQ:
dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
dev_info(info->dev, "Low Batt Warning(2) INTR\n");
break;
case WL1_IRQ:
dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
dev_info(info->dev, "Low Batt Warning(1) INTR\n");
break;
default:
dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
dev_warn(info->dev, "Spurious Interrupt!!!\n");
}
info->valid = 0; /* Force updating of the cached registers */
power_supply_changed(info->bat);
return IRQ_HANDLED;
}
......@@ -618,6 +487,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy)
{
struct axp288_fg_info *info = power_supply_get_drvdata(psy);
info->valid = 0; /* Force updating of the cached registers */
power_supply_changed(info->bat);
}
......@@ -632,16 +502,15 @@ static const struct power_supply_desc fuel_gauge_desc = {
.external_power_changed = fuel_gauge_external_power_changed,
};
static void fuel_gauge_init_irq(struct axp288_fg_info *info)
static void fuel_gauge_init_irq(struct axp288_fg_info *info, struct platform_device *pdev)
{
int ret, i, pirq;
for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
pirq = platform_get_irq(info->pdev, i);
pirq = platform_get_irq(pdev, i);
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
if (info->irq[i] < 0) {
dev_warn(&info->pdev->dev,
"regmap_irq get virq failed for IRQ %d: %d\n",
dev_warn(info->dev, "regmap_irq get virq failed for IRQ %d: %d\n",
pirq, info->irq[i]);
info->irq[i] = -1;
goto intr_failed;
......@@ -650,14 +519,10 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info)
NULL, fuel_gauge_thread_handler,
IRQF_ONESHOT, DEV_NAME, info);
if (ret) {
dev_warn(&info->pdev->dev,
"request irq failed for IRQ %d: %d\n",
dev_warn(info->dev, "request irq failed for IRQ %d: %d\n",
pirq, info->irq[i]);
info->irq[i] = -1;
goto intr_failed;
} else {
dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
pirq, info->irq[i]);
}
}
return;
......@@ -753,9 +618,6 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {};
static const char * const iio_chan_name[] = {
[BAT_TEMP] = "axp288-batt-temp",
[PMIC_TEMP] = "axp288-pmic-temp",
[SYSTEM_TEMP] = "axp288-system-temp",
[BAT_CHRG_CURR] = "axp288-chrg-curr",
[BAT_D_CURR] = "axp288-chrg-d-curr",
[BAT_VOLT] = "axp288-batt-volt",
......@@ -765,24 +627,15 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
if (dmi_check_system(axp288_no_battery_list))
return -ENODEV;
/*
* On some devices the fuelgauge and charger parts of the axp288 are
* not used, check that the fuelgauge is enabled (CC_CTRL != 0).
*/
ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
if (ret < 0)
return ret;
if (val == 0)
return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->pdev = pdev;
info->dev = &pdev->dev;
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
info->valid = 0;
platform_set_drvdata(pdev, info);
......@@ -808,19 +661,35 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
}
}
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
ret = iosf_mbi_block_punit_i2c_access();
if (ret < 0)
goto out_free_iio_chan;
/*
* On some devices the fuelgauge and charger parts of the axp288 are
* not used, check that the fuelgauge is enabled (CC_CTRL != 0).
*/
ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
if (ret < 0)
goto unblock_punit_i2c_access;
if (val == 0) {
ret = -ENODEV;
goto unblock_punit_i2c_access;
}
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
if (ret < 0)
goto unblock_punit_i2c_access;
if (!(ret & FG_DES_CAP1_VALID)) {
dev_err(&pdev->dev, "axp288 not configured by firmware\n");
ret = -ENODEV;
goto out_free_iio_chan;
goto unblock_punit_i2c_access;
}
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
if (ret < 0)
goto out_free_iio_chan;
goto unblock_punit_i2c_access;
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
case CHRG_CCCV_CV_4100MV:
info->max_volt = 4100;
......@@ -836,6 +705,22 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
break;
}
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
if (ret < 0)
goto unblock_punit_i2c_access;
info->pwr_op = ret;
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
if (ret < 0)
goto unblock_punit_i2c_access;
info->low_cap = ret;
unblock_punit_i2c_access:
iosf_mbi_unblock_punit_i2c_access();
/* In case we arrive here by goto because of a register access error */
if (ret < 0)
goto out_free_iio_chan;
psy_cfg.drv_data = info;
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
if (IS_ERR(info->bat)) {
......@@ -844,8 +729,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
goto out_free_iio_chan;
}
fuel_gauge_create_debugfs(info);
fuel_gauge_init_irq(info);
fuel_gauge_init_irq(info, pdev);
return 0;
......@@ -869,7 +753,6 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev)
int i;
power_supply_unregister(info->bat);
fuel_gauge_remove_debugfs(info);
for (i = 0; i < AXP288_FG_INTR_NUM; i++)
if (info->irq[i] >= 0)
......
......@@ -31,9 +31,8 @@
#include <linux/power/bq24735-charger.h>
#define BQ24735_CHG_OPT 0x12
#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4)
/* BQ24735 available commands and their respective masks */
#define BQ24735_CHARGE_OPT 0x12
#define BQ24735_CHARGE_CURRENT 0x14
#define BQ24735_CHARGE_CURRENT_MASK 0x1fc0
#define BQ24735_CHARGE_VOLTAGE 0x15
......@@ -43,6 +42,10 @@
#define BQ24735_MANUFACTURER_ID 0xfe
#define BQ24735_DEVICE_ID 0xff
/* ChargeOptions bits of interest */
#define BQ24735_CHARGE_OPT_CHG_DISABLE (1 << 0)
#define BQ24735_CHARGE_OPT_AC_PRESENT (1 << 4)
struct bq24735 {
struct power_supply *charger;
struct power_supply_desc charger_desc;
......@@ -167,8 +170,8 @@ static inline int bq24735_enable_charging(struct bq24735 *charger)
if (ret)
return ret;
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
BQ24735_CHARGE_OPT_CHG_DISABLE, 0);
}
static inline int bq24735_disable_charging(struct bq24735 *charger)
......@@ -176,9 +179,9 @@ static inline int bq24735_disable_charging(struct bq24735 *charger)
if (charger->pdata->ext_control)
return 0;
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
BQ24735_CHG_OPT_CHARGE_DISABLE,
BQ24735_CHG_OPT_CHARGE_DISABLE);
return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
BQ24735_CHARGE_OPT_CHG_DISABLE,
BQ24735_CHARGE_OPT_CHG_DISABLE);
}
static bool bq24735_charger_is_present(struct bq24735 *charger)
......@@ -188,14 +191,14 @@ static bool bq24735_charger_is_present(struct bq24735 *charger)
} else {
int ac = 0;
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
ac = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
if (ac < 0) {
dev_dbg(&charger->client->dev,
"Failed to read charger options : %d\n",
ac);
return false;
}
return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
return (ac & BQ24735_CHARGE_OPT_AC_PRESENT) ? true : false;
}
return false;
......@@ -208,11 +211,11 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)
if (!bq24735_charger_is_present(charger))
return 0;
ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
ret = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
if (ret < 0)
return ret;
return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
return !(ret & BQ24735_CHARGE_OPT_CHG_DISABLE);
}
static void bq24735_update(struct bq24735 *charger)
......
// SPDX-License-Identifier: GPL-2.0
/*
* Power supply driver for ChromeOS EC based Peripheral Device Charger.
*
* Copyright 2020 Google LLC.
*/
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/stringify.h>
#include <linux/types.h>
#define DRV_NAME "cros-ec-pchg"
#define PCHG_DIR_PREFIX "peripheral"
#define PCHG_DIR_NAME PCHG_DIR_PREFIX "%d"
#define PCHG_DIR_NAME_LENGTH \
sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
#define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
struct port_data {
int port_number;
char name[PCHG_DIR_NAME_LENGTH];
struct power_supply *psy;
struct power_supply_desc psy_desc;
int psy_status;
int battery_percentage;
int charge_type;
struct charger_data *charger;
unsigned long last_update;
};
struct charger_data {
struct device *dev;
struct cros_ec_dev *ec_dev;
struct cros_ec_device *ec_device;
int num_registered_psy;
struct port_data *ports[EC_PCHG_MAX_PORTS];
struct notifier_block notifier;
};
static enum power_supply_property cros_pchg_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SCOPE,
};
static int cros_pchg_ec_command(const struct charger_data *charger,
unsigned int version,
unsigned int command,
const void *outdata,
unsigned int outsize,
void *indata,
unsigned int insize)
{
struct cros_ec_dev *ec_dev = charger->ec_dev;
struct cros_ec_command *msg;
int ret;
msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = version;
msg->command = ec_dev->cmd_offset + command;
msg->outsize = outsize;
msg->insize = insize;
if (outsize)
memcpy(msg->data, outdata, outsize);
ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
if (ret >= 0 && insize)
memcpy(indata, msg->data, insize);
kfree(msg);
return ret;
}
static const unsigned int pchg_cmd_version = 1;
static bool cros_pchg_cmd_ver_check(const struct charger_data *charger)
{
struct ec_params_get_cmd_versions_v1 req;
struct ec_response_get_cmd_versions rsp;
int ret;
req.cmd = EC_CMD_PCHG;
ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret < 0) {
dev_warn(charger->dev,
"Unable to get versions of EC_CMD_PCHG (err:%d)\n",
ret);
return false;
}
return !!(rsp.version_mask & BIT(pchg_cmd_version));
}
static int cros_pchg_port_count(const struct charger_data *charger)
{
struct ec_response_pchg_count rsp;
int ret;
ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT,
NULL, 0, &rsp, sizeof(rsp));
if (ret < 0) {
dev_warn(charger->dev,
"Unable to get number or ports (err:%d)\n", ret);
return ret;
}
return rsp.port_count;
}
static int cros_pchg_get_status(struct port_data *port)
{
struct charger_data *charger = port->charger;
struct ec_params_pchg req;
struct ec_response_pchg rsp;
struct device *dev = charger->dev;
int old_status = port->psy_status;
int old_percentage = port->battery_percentage;
int ret;
req.port = port->port_number;
ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret < 0) {
dev_err(dev, "Unable to get port.%d status (err:%d)\n",
port->port_number, ret);
return ret;
}
switch (rsp.state) {
case PCHG_STATE_RESET:
case PCHG_STATE_INITIALIZED:
case PCHG_STATE_ENABLED:
default:
port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN;
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
case PCHG_STATE_DETECTED:
port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case PCHG_STATE_CHARGING:
port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
break;
case PCHG_STATE_FULL:
port->psy_status = POWER_SUPPLY_STATUS_FULL;
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
}
port->battery_percentage = rsp.battery_percentage;
if (port->psy_status != old_status ||
port->battery_percentage != old_percentage)
power_supply_changed(port->psy);
dev_dbg(dev,
"Port %d: state=%d battery=%d%%\n",
port->port_number, rsp.state, rsp.battery_percentage);
return 0;
}
static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit)
{
int ret;
if (ratelimit &&
time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY))
return 0;
ret = cros_pchg_get_status(port);
if (ret < 0)
return ret;
port->last_update = jiffies;
return ret;
}
static int cros_pchg_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct port_data *port = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
case POWER_SUPPLY_PROP_CAPACITY:
case POWER_SUPPLY_PROP_CHARGE_TYPE:
cros_pchg_get_port_status(port, true);
break;
default:
break;
}
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = port->psy_status;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = port->battery_percentage;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = port->charge_type;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
default:
return -EINVAL;
}
return 0;
}
static int cros_pchg_event(const struct charger_data *charger,
unsigned long host_event)
{
int i;
for (i = 0; i < charger->num_registered_psy; i++)
cros_pchg_get_port_status(charger->ports[i], false);
return NOTIFY_OK;
}
static u32 cros_get_device_event(const struct charger_data *charger)
{
struct ec_params_device_event req;
struct ec_response_device_event rsp;
struct device *dev = charger->dev;
int ret;
req.param = EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS;
ret = cros_pchg_ec_command(charger, 0, EC_CMD_DEVICE_EVENT,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret < 0) {
dev_warn(dev, "Unable to get device events (err:%d)\n", ret);
return 0;
}
return rsp.event_mask;
}
static int cros_ec_notify(struct notifier_block *nb,
unsigned long queued_during_suspend,
void *data)
{
struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
u32 host_event = cros_ec_get_host_event(ec_dev);
struct charger_data *charger =
container_of(nb, struct charger_data, notifier);
u32 device_event_mask;
if (!host_event)
return NOTIFY_DONE;
if (!(host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_DEVICE)))
return NOTIFY_DONE;
/*
* todo: Retrieve device event mask in common place
* (e.g. cros_ec_proto.c).
*/
device_event_mask = cros_get_device_event(charger);
if (!(device_event_mask & EC_DEVICE_EVENT_MASK(EC_DEVICE_EVENT_WLC)))
return NOTIFY_DONE;
return cros_pchg_event(charger, host_event);
}
static int cros_pchg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *ec_device = ec_dev->ec_dev;
struct power_supply_desc *psy_desc;
struct charger_data *charger;
struct power_supply *psy;
struct port_data *port;
struct notifier_block *nb;
int num_ports;
int ret;
int i;
charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
if (!charger)
return -ENOMEM;
charger->dev = dev;
charger->ec_dev = ec_dev;
charger->ec_device = ec_device;
ret = cros_pchg_port_count(charger);
if (ret <= 0) {
/*
* This feature is enabled by the EC and the kernel driver is
* included by default for CrOS devices. Don't need to be loud
* since this error can be normal.
*/
dev_info(dev, "No peripheral charge ports (err:%d)\n", ret);
return -ENODEV;
}
if (!cros_pchg_cmd_ver_check(charger)) {
dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n",
pchg_cmd_version);
return -EOPNOTSUPP;
}
num_ports = ret;
if (num_ports > EC_PCHG_MAX_PORTS) {
dev_err(dev, "Too many peripheral charge ports (%d)\n",
num_ports);
return -ENOBUFS;
}
dev_info(dev, "%d peripheral charge ports found\n", num_ports);
for (i = 0; i < num_ports; i++) {
struct power_supply_config psy_cfg = {};
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
port->charger = charger;
port->port_number = i;
snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i);
psy_desc = &port->psy_desc;
psy_desc->name = port->name;
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
psy_desc->get_property = cros_pchg_get_prop;
psy_desc->external_power_changed = NULL;
psy_desc->properties = cros_pchg_props;
psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props);
psy_cfg.drv_data = port;
psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
if (IS_ERR(psy))
return dev_err_probe(dev, PTR_ERR(psy),
"Failed to register power supply\n");
port->psy = psy;
charger->ports[charger->num_registered_psy++] = port;
}
if (!charger->num_registered_psy)
return -ENODEV;
nb = &charger->notifier;
nb->notifier_call = cros_ec_notify;
ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
nb);
if (ret < 0)
dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
return 0;
}
static struct platform_driver cros_pchg_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_pchg_probe
};
module_platform_driver(cros_pchg_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");
MODULE_ALIAS("platform:" DRV_NAME);
......@@ -679,7 +679,9 @@ static int cw_bat_probe(struct i2c_client *client)
&cw2015_bat_desc,
&psy_cfg);
if (IS_ERR(cw_bat->rk_bat)) {
dev_err(cw_bat->dev, "Failed to register power supply\n");
/* try again if this happens */
dev_err_probe(&client->dev, PTR_ERR(cw_bat->rk_bat),
"Failed to register power supply\n");
return PTR_ERR(cw_bat->rk_bat);
}
......
......@@ -36,8 +36,6 @@
/* Interrupt mask bits */
#define CONFIG_ALRT_BIT_ENBL (1 << 2)
#define STATUS_INTR_SOCMIN_BIT (1 << 10)
#define STATUS_INTR_SOCMAX_BIT (1 << 14)
#define VFSOC0_LOCK 0x0000
#define VFSOC0_UNLOCK 0x0080
......@@ -285,8 +283,6 @@ static int max17042_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
ret = regmap_read(map, MAX17042_V_empty, &data);
else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
ret = regmap_read(map, MAX17055_V_empty, &data);
else
ret = regmap_read(map, MAX17047_V_empty, &data);
if (ret < 0)
......@@ -748,7 +744,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
struct max17042_config_data *config = chip->pdata->config_data;
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
max17042_override_por(map, MAx17042_TOFF, config->toff);
max17042_override_por(map, MAX17042_TOFF, config->toff);
max17042_override_por(map, MAX17042_CGAIN, config->cgain);
max17042_override_por(map, MAX17042_COFF, config->coff);
......@@ -767,36 +763,36 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
max17042_override_por(map, MAX17042_SOC_empty,
config->socempty);
max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
max17042_override_por(map, MAX17042_V_empty, config->vempty);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
max17042_override_por(map, MAX17055_V_empty, config->vempty);
else
max17042_override_por(map, MAX17047_V_empty, config->vempty);
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
max17042_override_por(map, MAX17042_FCTC, config->fctc);
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
if (chip->chip_type &&
((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
max17042_override_por(map, MAX17042_SOC_empty, config->socempty);
max17042_override_por(map, MAX17042_V_empty, config->vempty);
max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco);
max17042_override_por(map, MAX17042_K_empty0, config->kempty0);
}
if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) {
max17042_override_por(map, MAX17042_EmptyTempCo,
config->empty_tempco);
max17042_override_por(map, MAX17042_K_empty0,
config->kempty0);
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
max17042_override_por(map, MAX17042_FCTC, config->fctc);
}
if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) {
max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
}
......@@ -869,11 +865,14 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
u32 val;
int ret;
regmap_read(chip->regmap, MAX17042_STATUS, &val);
if ((val & STATUS_INTR_SOCMIN_BIT) ||
(val & STATUS_INTR_SOCMAX_BIT)) {
dev_info(&chip->client->dev, "SOC threshold INTR\n");
ret = regmap_read(chip->regmap, MAX17042_STATUS, &val);
if (ret)
return IRQ_HANDLED;
if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) {
dev_dbg(&chip->client->dev, "SOC threshold INTR\n");
max17042_set_soc_threshold(chip, 1);
}
......@@ -1196,6 +1195,7 @@ static const struct of_device_id max17042_dt_match[] = {
{ .compatible = "maxim,max17047" },
{ .compatible = "maxim,max17050" },
{ .compatible = "maxim,max17055" },
{ .compatible = "maxim,max77849-battery" },
{ },
};
MODULE_DEVICE_TABLE(of, max17042_dt_match);
......@@ -1206,6 +1206,7 @@ static const struct i2c_device_id max17042_id[] = {
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
{ "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17042_id);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/devm-helpers.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/linear_range.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#define MT6360_PMU_CHG_CTRL1 0x311
#define MT6360_PMU_CHG_CTRL2 0x312
#define MT6360_PMU_CHG_CTRL3 0x313
#define MT6360_PMU_CHG_CTRL4 0x314
#define MT6360_PMU_CHG_CTRL5 0x315
#define MT6360_PMU_CHG_CTRL6 0x316
#define MT6360_PMU_CHG_CTRL7 0x317
#define MT6360_PMU_CHG_CTRL8 0x318
#define MT6360_PMU_CHG_CTRL9 0x319
#define MT6360_PMU_CHG_CTRL10 0x31A
#define MT6360_PMU_DEVICE_TYPE 0x322
#define MT6360_PMU_USB_STATUS1 0x327
#define MT6360_PMU_CHG_STAT 0x34A
#define MT6360_PMU_CHG_CTRL19 0x361
#define MT6360_PMU_FOD_STAT 0x3E7
/* MT6360_PMU_CHG_CTRL1 */
#define MT6360_FSLP_SHFT (3)
#define MT6360_FSLP_MASK BIT(MT6360_FSLP_SHFT)
#define MT6360_OPA_MODE_SHFT (0)
#define MT6360_OPA_MODE_MASK BIT(MT6360_OPA_MODE_SHFT)
/* MT6360_PMU_CHG_CTRL2 */
#define MT6360_IINLMTSEL_SHFT (2)
#define MT6360_IINLMTSEL_MASK GENMASK(3, 2)
/* MT6360_PMU_CHG_CTRL3 */
#define MT6360_IAICR_SHFT (2)
#define MT6360_IAICR_MASK GENMASK(7, 2)
#define MT6360_ILIM_EN_MASK BIT(0)
/* MT6360_PMU_CHG_CTRL4 */
#define MT6360_VOREG_SHFT (1)
#define MT6360_VOREG_MASK GENMASK(7, 1)
/* MT6360_PMU_CHG_CTRL5 */
#define MT6360_VOBST_MASK GENMASK(7, 2)
/* MT6360_PMU_CHG_CTRL6 */
#define MT6360_VMIVR_SHFT (1)
#define MT6360_VMIVR_MASK GENMASK(7, 1)
/* MT6360_PMU_CHG_CTRL7 */
#define MT6360_ICHG_SHFT (2)
#define MT6360_ICHG_MASK GENMASK(7, 2)
/* MT6360_PMU_CHG_CTRL8 */
#define MT6360_IPREC_SHFT (0)
#define MT6360_IPREC_MASK GENMASK(3, 0)
/* MT6360_PMU_CHG_CTRL9 */
#define MT6360_IEOC_SHFT (4)
#define MT6360_IEOC_MASK GENMASK(7, 4)
/* MT6360_PMU_CHG_CTRL10 */
#define MT6360_OTG_OC_MASK GENMASK(3, 0)
/* MT6360_PMU_DEVICE_TYPE */
#define MT6360_USBCHGEN_MASK BIT(7)
/* MT6360_PMU_USB_STATUS1 */
#define MT6360_USB_STATUS_SHFT (4)
#define MT6360_USB_STATUS_MASK GENMASK(6, 4)
/* MT6360_PMU_CHG_STAT */
#define MT6360_CHG_STAT_SHFT (6)
#define MT6360_CHG_STAT_MASK GENMASK(7, 6)
#define MT6360_VBAT_LVL_MASK BIT(5)
/* MT6360_PMU_CHG_CTRL19 */
#define MT6360_VINOVP_SHFT (5)
#define MT6360_VINOVP_MASK GENMASK(6, 5)
/* MT6360_PMU_FOD_STAT */
#define MT6360_CHRDET_EXT_MASK BIT(4)
/* uV */
#define MT6360_VMIVR_MIN 3900000
#define MT6360_VMIVR_MAX 13400000
#define MT6360_VMIVR_STEP 100000
/* uA */
#define MT6360_ICHG_MIN 100000
#define MT6360_ICHG_MAX 5000000
#define MT6360_ICHG_STEP 100000
/* uV */
#define MT6360_VOREG_MIN 3900000
#define MT6360_VOREG_MAX 4710000
#define MT6360_VOREG_STEP 10000
/* uA */
#define MT6360_AICR_MIN 100000
#define MT6360_AICR_MAX 3250000
#define MT6360_AICR_STEP 50000
/* uA */
#define MT6360_IPREC_MIN 100000
#define MT6360_IPREC_MAX 850000
#define MT6360_IPREC_STEP 50000
/* uA */
#define MT6360_IEOC_MIN 100000
#define MT6360_IEOC_MAX 850000
#define MT6360_IEOC_STEP 50000
enum {
MT6360_RANGE_VMIVR,
MT6360_RANGE_ICHG,
MT6360_RANGE_VOREG,
MT6360_RANGE_AICR,
MT6360_RANGE_IPREC,
MT6360_RANGE_IEOC,
MT6360_RANGE_MAX,
};
#define MT6360_LINEAR_RANGE(idx, _min, _min_sel, _max_sel, _step) \
[idx] = REGULATOR_LINEAR_RANGE(_min, _min_sel, _max_sel, _step)
static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = {
MT6360_LINEAR_RANGE(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000),
MT6360_LINEAR_RANGE(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000),
MT6360_LINEAR_RANGE(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000),
MT6360_LINEAR_RANGE(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000),
MT6360_LINEAR_RANGE(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000),
MT6360_LINEAR_RANGE(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000),
};
struct mt6360_chg_info {
struct device *dev;
struct regmap *regmap;
struct power_supply_desc psy_desc;
struct power_supply *psy;
struct regulator_dev *otg_rdev;
struct mutex chgdet_lock;
u32 vinovp;
bool pwr_rdy;
bool bc12_en;
int psy_usb_type;
struct work_struct chrdet_work;
};
enum mt6360_iinlmtsel {
MT6360_IINLMTSEL_AICR_3250 = 0,
MT6360_IINLMTSEL_CHG_TYPE,
MT6360_IINLMTSEL_AICR,
MT6360_IINLMTSEL_LOWER_LEVEL,
};
enum mt6360_pmu_chg_type {
MT6360_CHG_TYPE_NOVBUS = 0,
MT6360_CHG_TYPE_UNDER_GOING,
MT6360_CHG_TYPE_SDP,
MT6360_CHG_TYPE_SDPNSTD,
MT6360_CHG_TYPE_DCP,
MT6360_CHG_TYPE_CDP,
MT6360_CHG_TYPE_DISABLE_BC12,
MT6360_CHG_TYPE_MAX,
};
static enum power_supply_usb_type mt6360_charger_usb_types[] = {
POWER_SUPPLY_USB_TYPE_UNKNOWN,
POWER_SUPPLY_USB_TYPE_SDP,
POWER_SUPPLY_USB_TYPE_DCP,
POWER_SUPPLY_USB_TYPE_CDP,
};
static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci,
bool *pwr_rdy)
{
int ret;
unsigned int regval;
ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, &regval);
if (ret < 0)
return ret;
*pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false;
return 0;
}
static int mt6360_charger_get_online(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
bool pwr_rdy;
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
if (ret < 0)
return ret;
val->intval = pwr_rdy ? true : false;
return 0;
}
static int mt6360_charger_get_status(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int status, ret;
unsigned int regval;
bool pwr_rdy;
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
if (ret < 0)
return ret;
if (!pwr_rdy) {
status = POWER_SUPPLY_STATUS_DISCHARGING;
goto out;
}
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
if (ret < 0)
return ret;
regval &= MT6360_CHG_STAT_MASK;
regval >>= MT6360_CHG_STAT_SHFT;
switch (regval) {
case 0x0:
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case 0x1:
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x2:
status = POWER_SUPPLY_STATUS_FULL;
break;
default:
ret = -EIO;
}
out:
if (!ret)
val->intval = status;
return ret;
}
static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int type, ret;
unsigned int regval;
u8 chg_stat;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
if (ret < 0)
return ret;
chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT;
switch (chg_stat) {
case 0x01: /* Charge in Progress */
if (regval & MT6360_VBAT_LVL_MASK)
type = POWER_SUPPLY_CHARGE_TYPE_FAST;
else
type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case 0x00: /* Not Charging */
case 0x02: /* Charge Done */
case 0x03: /* Charge Fault */
default:
type = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
}
val->intval = type;
return 0;
}
static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
val->intval = MT6360_ICHG_MAX;
return 0;
}
static int mt6360_charger_get_cv(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
val->intval = MT6360_VOREG_MAX;
return 0;
}
static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci,
union power_supply_propval *val)
{
int ret;
u32 sel, value;
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel);
if (ret < 0)
return ret;
sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT;
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value);
if (!ret)
val->intval = value;
return ret;
}
static int mt6360_charger_set_online(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u8 force_sleep = val->intval ? 0 : 1;
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL1,
MT6360_FSLP_MASK,
force_sleep << MT6360_FSLP_SHFT);
}
static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL7,
MT6360_ICHG_MASK,
sel << MT6360_ICHG_SHFT);
}
static int mt6360_charger_set_cv(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL4,
MT6360_VOREG_MASK,
sel << MT6360_VOREG_SHFT);
}
static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL3,
MT6360_IAICR_MASK,
sel << MT6360_IAICR_SHFT);
}
static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL3,
MT6360_VMIVR_MASK,
sel << MT6360_VMIVR_SHFT);
}
static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL8,
MT6360_IPREC_MASK,
sel << MT6360_IPREC_SHFT);
}
static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci,
const union power_supply_propval *val)
{
u32 sel;
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel);
return regmap_update_bits(mci->regmap,
MT6360_PMU_CHG_CTRL9,
MT6360_IEOC_MASK,
sel << MT6360_IEOC_SHFT);
}
static int mt6360_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = mt6360_charger_get_online(mci, val);
break;
case POWER_SUPPLY_PROP_STATUS:
ret = mt6360_charger_get_status(mci, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = mt6360_charger_get_charge_type(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = mt6360_charger_get_ichg(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
ret = mt6360_charger_get_max_ichg(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = mt6360_charger_get_cv(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
ret = mt6360_charger_get_max_cv(mci, val);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = mt6360_charger_get_aicr(mci, val);
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
ret = mt6360_charger_get_mivr(mci, val);
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
ret = mt6360_charger_get_iprechg(mci, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
ret = mt6360_charger_get_ieoc(mci, val);
break;
case POWER_SUPPLY_PROP_USB_TYPE:
val->intval = mci->psy_usb_type;
break;
default:
ret = -ENODATA;
}
return ret;
}
static int mt6360_charger_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
ret = mt6360_charger_set_online(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = mt6360_charger_set_ichg(mci, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = mt6360_charger_set_cv(mci, val);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = mt6360_charger_set_aicr(mci, val);
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
ret = mt6360_charger_set_mivr(mci, val);
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
ret = mt6360_charger_set_iprechg(mci, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
ret = mt6360_charger_set_ieoc(mci, val);
break;
default:
ret = -EINVAL;
}
return ret;
}
static int mt6360_charger_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
return 1;
default:
return 0;
}
}
static enum power_supply_property mt6360_charger_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_USB_TYPE,
};
static const struct power_supply_desc mt6360_charger_desc = {
.type = POWER_SUPPLY_TYPE_USB,
.properties = mt6360_charger_properties,
.num_properties = ARRAY_SIZE(mt6360_charger_properties),
.get_property = mt6360_charger_get_property,
.set_property = mt6360_charger_set_property,
.property_is_writeable = mt6360_charger_property_is_writeable,
.usb_types = mt6360_charger_usb_types,
.num_usb_types = ARRAY_SIZE(mt6360_charger_usb_types),
};
static const struct regulator_ops mt6360_chg_otg_ops = {
.list_voltage = regulator_list_voltage_linear,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
static const struct regulator_desc mt6360_otg_rdesc = {
.of_match = "usb-otg-vbus",
.name = "usb-otg-vbus",
.ops = &mt6360_chg_otg_ops,
.owner = THIS_MODULE,
.type = REGULATOR_VOLTAGE,
.min_uV = 4425000,
.uV_step = 25000,
.n_voltages = 57,
.vsel_reg = MT6360_PMU_CHG_CTRL5,
.vsel_mask = MT6360_VOBST_MASK,
.enable_reg = MT6360_PMU_CHG_CTRL1,
.enable_mask = MT6360_OPA_MODE_MASK,
};
static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data)
{
struct mt6360_chg_info *mci = data;
int ret;
unsigned int usb_status;
int last_usb_type;
mutex_lock(&mci->chgdet_lock);
if (!mci->bc12_en) {
dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n");
goto out;
}
last_usb_type = mci->psy_usb_type;
/* Plug in */
ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status);
if (ret < 0)
goto out;
usb_status &= MT6360_USB_STATUS_MASK;
usb_status >>= MT6360_USB_STATUS_SHFT;
switch (usb_status) {
case MT6360_CHG_TYPE_NOVBUS:
dev_dbg(mci->dev, "Received attach interrupt, no vbus\n");
goto out;
case MT6360_CHG_TYPE_UNDER_GOING:
dev_dbg(mci->dev, "Received attach interrupt, under going...\n");
goto out;
case MT6360_CHG_TYPE_SDP:
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
break;
case MT6360_CHG_TYPE_SDPNSTD:
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
break;
case MT6360_CHG_TYPE_CDP:
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
break;
case MT6360_CHG_TYPE_DCP:
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
break;
case MT6360_CHG_TYPE_DISABLE_BC12:
dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n");
goto out;
default:
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
dev_dbg(mci->dev, "Received attach interrupt, reserved address\n");
goto out;
}
dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type);
if (last_usb_type != mci->psy_usb_type)
power_supply_changed(mci->psy);
out:
mutex_unlock(&mci->chgdet_lock);
return IRQ_HANDLED;
}
static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci)
{
int ret;
bool pwr_rdy;
mutex_lock(&mci->chgdet_lock);
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
if (ret < 0)
goto out;
if (mci->pwr_rdy == pwr_rdy) {
dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy);
goto out;
}
mci->pwr_rdy = pwr_rdy;
dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy);
if (!pwr_rdy) {
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
power_supply_changed(mci->psy);
}
ret = regmap_update_bits(mci->regmap,
MT6360_PMU_DEVICE_TYPE,
MT6360_USBCHGEN_MASK,
pwr_rdy ? MT6360_USBCHGEN_MASK : 0);
if (ret < 0)
goto out;
mci->bc12_en = pwr_rdy;
out:
mutex_unlock(&mci->chgdet_lock);
}
static void mt6360_chrdet_work(struct work_struct *work)
{
struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of(
work, struct mt6360_chg_info, chrdet_work);
mt6360_handle_chrdet_ext_evt(mci);
}
static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data)
{
struct mt6360_chg_info *mci = data;
mt6360_handle_chrdet_ext_evt(mci);
return IRQ_HANDLED;
}
static int mt6360_chg_irq_register(struct platform_device *pdev)
{
const struct {
const char *name;
irq_handler_t handler;
} irq_descs[] = {
{ "attach_i", mt6360_pmu_attach_i_handler },
{ "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler }
};
int i, ret;
for (i = 0; i < ARRAY_SIZE(irq_descs); i++) {
ret = platform_get_irq_byname(pdev, irq_descs[i].name);
if (ret < 0)
return ret;
ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
irq_descs[i].handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
irq_descs[i].name,
platform_get_drvdata(pdev));
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n",
irq_descs[i].name);
}
return 0;
}
static u32 mt6360_vinovp_trans_to_sel(u32 val)
{
u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 };
int i;
/* Select the smaller and equal supported value */
for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) {
if (val < vinovp_tbl[i+1])
break;
}
return i;
}
static int mt6360_chg_init_setting(struct mt6360_chg_info *mci)
{
int ret;
u32 sel;
sel = mt6360_vinovp_trans_to_sel(mci->vinovp);
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19,
MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT);
if (ret)
return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__);
ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE,
MT6360_USBCHGEN_MASK, 0);
if (ret)
return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__);
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2,
MT6360_IINLMTSEL_MASK,
MT6360_IINLMTSEL_AICR <<
MT6360_IINLMTSEL_SHFT);
if (ret)
return dev_err_probe(mci->dev, ret,
"%s: Failed to switch iinlmtsel to aicr\n", __func__);
usleep_range(5000, 6000);
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3,
MT6360_ILIM_EN_MASK, 0);
if (ret)
return dev_err_probe(mci->dev, ret,
"%s: Failed to disable ilim\n", __func__);
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10,
MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK);
if (ret)
return dev_err_probe(mci->dev, ret,
"%s: Failed to config otg oc to 3A\n", __func__);
return 0;
}
static int mt6360_charger_probe(struct platform_device *pdev)
{
struct mt6360_chg_info *mci;
struct power_supply_config charger_cfg = {};
struct regulator_config config = { };
int ret;
mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL);
if (!mci)
return -ENOMEM;
mci->dev = &pdev->dev;
mci->vinovp = 6500000;
mutex_init(&mci->chgdet_lock);
platform_set_drvdata(pdev, mci);
devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp);
if (ret)
dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n");
mci->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!mci->regmap)
return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n");
ret = mt6360_chg_init_setting(mci);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n");
memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc));
mci->psy_desc.name = dev_name(&pdev->dev);
charger_cfg.drv_data = mci;
charger_cfg.of_node = pdev->dev.of_node;
mci->psy = devm_power_supply_register(&pdev->dev,
&mci->psy_desc, &charger_cfg);
if (IS_ERR(mci->psy))
return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy),
"Failed to register power supply dev\n");
ret = mt6360_chg_irq_register(pdev);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n");
config.dev = &pdev->dev;
config.regmap = mci->regmap;
mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc,
&config);
if (IS_ERR(mci->otg_rdev))
return PTR_ERR(mci->otg_rdev);
schedule_work(&mci->chrdet_work);
return 0;
}
static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = {
{ .compatible = "mediatek,mt6360-chg", },
{},
};
MODULE_DEVICE_TABLE(of, mt6360_charger_of_id);
static const struct platform_device_id mt6360_charger_id[] = {
{ "mt6360-chg", 0 },
{},
};
MODULE_DEVICE_TABLE(platform, mt6360_charger_id);
static struct platform_driver mt6360_charger_driver = {
.driver = {
.name = "mt6360-chg",
.of_match_table = of_match_ptr(mt6360_charger_of_id),
},
.probe = mt6360_charger_probe,
.id_table = mt6360_charger_id,
};
module_platform_driver(mt6360_charger_driver);
MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
MODULE_DESCRIPTION("MT6360 Charger Driver");
MODULE_LICENSE("GPL");
......@@ -571,6 +571,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
int err, len, index;
const __be32 *list;
info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL;
info->voltage_min_design_uv = -EINVAL;
......@@ -618,6 +619,24 @@ int power_supply_get_battery_info(struct power_supply *psy,
* Documentation/power/power_supply_class.rst.
*/
if (!of_property_read_string(battery_np, "device-chemistry", &value)) {
if (!strcmp("nickel-cadmium", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
else if (!strcmp("nickel-metal-hydride", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
else if (!strcmp("lithium-ion", value))
/* Imprecise lithium-ion type */
info->technology = POWER_SUPPLY_TECHNOLOGY_LION;
else if (!strcmp("lithium-ion-polymer", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
else if (!strcmp("lithium-ion-iron-phosphate", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
else if (!strcmp("lithium-ion-manganese-oxide", value))
info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
else
dev_warn(&psy->dev, "%s unknown battery type\n", value);
}
of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
&info->energy_full_design_uwh);
of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
......
......@@ -929,11 +929,8 @@ static int smbb_charger_probe(struct platform_device *pdev)
int irq;
irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq '%s'\n",
smbb_charger_irqs[i].name);
if (irq < 0)
return irq;
}
smbb_charger_irqs[i].handler(irq, chg);
......
......@@ -9,10 +9,12 @@
#include <linux/device.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/iio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mfd/rn5t618.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
......@@ -64,6 +66,8 @@ struct rn5t618_power_info {
struct power_supply *battery;
struct power_supply *usb;
struct power_supply *adp;
struct iio_channel *channel_vusb;
struct iio_channel *channel_vadp;
int irq;
};
......@@ -77,6 +81,7 @@ static enum power_supply_usb_type rn5t618_usb_types[] = {
static enum power_supply_property rn5t618_usb_props[] = {
/* input current limit is not very accurate */
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
......@@ -85,6 +90,7 @@ static enum power_supply_property rn5t618_usb_props[] = {
static enum power_supply_property rn5t618_adp_props[] = {
/* input current limit is not very accurate */
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
};
......@@ -463,6 +469,15 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
return ret;
val->intval = FROM_CUR_REG(regval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (!info->channel_vadp)
return -ENODATA;
ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
......@@ -588,6 +603,15 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
val->intval = FROM_CUR_REG(regval);
}
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (!info->channel_vusb)
return -ENODATA;
ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
......@@ -711,6 +735,20 @@ static int rn5t618_power_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb");
if (IS_ERR(info->channel_vusb)) {
if (PTR_ERR(info->channel_vusb) == -ENODEV)
return -EPROBE_DEFER;
return PTR_ERR(info->channel_vusb);
}
info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp");
if (IS_ERR(info->channel_vadp)) {
if (PTR_ERR(info->channel_vadp) == -ENODEV)
return -EPROBE_DEFER;
return PTR_ERR(info->channel_vadp);
}
ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
if (ret)
return ret;
......
......@@ -31,8 +31,9 @@ enum {
REG_CURRENT_AVG,
REG_MAX_ERR,
REG_CAPACITY,
REG_TIME_TO_EMPTY,
REG_TIME_TO_FULL,
REG_TIME_TO_EMPTY_NOW,
REG_TIME_TO_EMPTY_AVG,
REG_TIME_TO_FULL_AVG,
REG_STATUS,
REG_CAPACITY_LEVEL,
REG_CYCLE_COUNT,
......@@ -102,7 +103,7 @@ static const struct chip_data {
[REG_TEMPERATURE] =
SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
[REG_VOLTAGE] =
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 65535),
[REG_CURRENT_NOW] =
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
[REG_CURRENT_AVG] =
......@@ -119,9 +120,11 @@ static const struct chip_data {
SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
[REG_FULL_CHARGE_CAPACITY_CHARGE] =
SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
[REG_TIME_TO_EMPTY] =
[REG_TIME_TO_EMPTY_NOW] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 0x11, 0, 65535),
[REG_TIME_TO_EMPTY_AVG] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
[REG_TIME_TO_FULL] =
[REG_TIME_TO_FULL_AVG] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
[REG_CHARGE_CURRENT] =
SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535),
......@@ -165,6 +168,7 @@ static const enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
......@@ -748,6 +752,7 @@ static void sbs_unit_adjustment(struct i2c_client *client,
val->intval -= TEMP_KELVIN_TO_CELSIUS;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
/* sbs provides time to empty and time to full in minutes.
......@@ -966,6 +971,7 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
......
......@@ -1229,10 +1229,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq resource specified\n");
if (irq < 0)
return irq;
}
ret = devm_request_threaded_irq(data->dev, irq, NULL,
sc27xx_fgu_interrupt,
......
......@@ -18,6 +18,7 @@
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <dt-bindings/power/summit,smb347-charger.h>
......@@ -55,6 +56,7 @@
#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
#define CFG_PIN_EN_APSD_IRQ BIT(1)
#define CFG_PIN_EN_CHARGER_ERROR BIT(2)
#define CFG_PIN_EN_CTRL BIT(4)
#define CFG_THERM 0x07
#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
......@@ -62,12 +64,15 @@
#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
#define CFG_THERM_MONITOR_DISABLED BIT(4)
#define CFG_SYSOK 0x08
#define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0)
#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
#define CFG_OTHER 0x09
#define CFG_OTHER_RID_MASK 0xc0
#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
#define CFG_OTG 0x0a
#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
#define CFG_OTG_CURRENT_LIMIT_250mA BIT(2)
#define CFG_OTG_CURRENT_LIMIT_750mA BIT(3)
#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
#define CFG_OTG_CC_COMPENSATION_MASK 0xc0
#define CFG_OTG_CC_COMPENSATION_SHIFT 6
......@@ -91,6 +96,7 @@
#define CMD_A 0x30
#define CMD_A_CHG_ENABLED BIT(1)
#define CMD_A_SUSPEND_ENABLED BIT(2)
#define CMD_A_OTG_ENABLED BIT(4)
#define CMD_A_ALLOW_WRITE BIT(7)
#define CMD_B 0x31
#define CMD_C 0x33
......@@ -132,11 +138,12 @@
* @regmap: pointer to driver regmap
* @mains: power_supply instance for AC/DC power
* @usb: power_supply instance for USB power
* @usb_rdev: USB VBUS regulator device
* @id: SMB charger ID
* @mains_online: is AC/DC input connected
* @usb_online: is USB input connected
* @charging_enabled: is charging enabled
* @irq_unsupported: is interrupt unsupported by SMB hardware
* @usb_vbus_enabled: is USB VBUS powered by SMB charger
* @max_charge_current: maximum current (in uA) the battery can be charged
* @max_charge_voltage: maximum voltage (in uV) the battery can be charged
* @pre_charge_current: current (in uA) to use in pre-charging phase
......@@ -167,6 +174,8 @@
* @use_usb_otg: USB OTG output can be used (not implemented yet)
* @enable_control: how charging enable/disable is controlled
* (driver/pin controls)
* @inok_polarity: polarity of INOK signal which denotes presence of external
* power supply
*
* @use_main, @use_usb, and @use_usb_otg are means to enable/disable
* hardware support for these. This is useful when we want to have for
......@@ -189,11 +198,12 @@ struct smb347_charger {
struct regmap *regmap;
struct power_supply *mains;
struct power_supply *usb;
struct regulator_dev *usb_rdev;
unsigned int id;
bool mains_online;
bool usb_online;
bool charging_enabled;
bool irq_unsupported;
bool usb_vbus_enabled;
unsigned int max_charge_current;
unsigned int max_charge_voltage;
......@@ -214,6 +224,7 @@ struct smb347_charger {
bool use_usb;
bool use_usb_otg;
unsigned int enable_control;
unsigned int inok_polarity;
};
enum smb_charger_chipid {
......@@ -358,21 +369,18 @@ static int smb347_charging_status(struct smb347_charger *smb)
static int smb347_charging_set(struct smb347_charger *smb, bool enable)
{
int ret = 0;
if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) {
dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
return 0;
}
if (smb->charging_enabled != enable) {
ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
enable ? CMD_A_CHG_ENABLED : 0);
if (!ret)
smb->charging_enabled = enable;
if (enable && smb->usb_vbus_enabled) {
dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n");
return 0;
}
return ret;
return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
enable ? CMD_A_CHG_ENABLED : 0);
}
static inline int smb347_charging_enable(struct smb347_charger *smb)
......@@ -671,10 +679,22 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
*
* Returns %0 on success and negative errno in case of failure.
*/
static int smb347_set_writable(struct smb347_charger *smb, bool writable)
static int smb347_set_writable(struct smb347_charger *smb, bool writable,
bool irq_toggle)
{
return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
writable ? CMD_A_ALLOW_WRITE : 0);
struct i2c_client *client = to_i2c_client(smb->dev);
int ret;
if (writable && irq_toggle && !smb->irq_unsupported)
disable_irq(client->irq);
ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
writable ? CMD_A_ALLOW_WRITE : 0);
if ((!writable || ret) && irq_toggle && !smb->irq_unsupported)
enable_irq(client->irq);
return ret;
}
static int smb347_hw_init(struct smb347_charger *smb)
......@@ -682,7 +702,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
unsigned int val;
int ret;
ret = smb347_set_writable(smb, true);
ret = smb347_set_writable(smb, true, false);
if (ret < 0)
return ret;
......@@ -724,6 +744,15 @@ static int smb347_hw_init(struct smb347_charger *smb)
if (ret < 0)
goto fail;
/* Activate pin control, making it writable. */
switch (smb->enable_control) {
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL);
if (ret < 0)
goto fail;
}
/*
* Make the charging functionality controllable by a write to the
* command register unless pin control is specified in the platform
......@@ -758,7 +787,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
ret = smb347_start_stop_charging(smb);
fail:
smb347_set_writable(smb, false);
smb347_set_writable(smb, false, false);
return ret;
}
......@@ -866,7 +895,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
if (smb->irq_unsupported)
return 0;
ret = smb347_set_writable(smb, true);
ret = smb347_set_writable(smb, true, true);
if (ret < 0)
return ret;
......@@ -891,7 +920,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
fail:
smb347_set_writable(smb, false);
smb347_set_writable(smb, false, true);
return ret;
}
......@@ -919,7 +948,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
if (!client->irq)
return 0;
ret = smb347_set_writable(smb, true);
ret = smb347_set_writable(smb, true, false);
if (ret < 0)
return ret;
......@@ -931,7 +960,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
CFG_STAT_DISABLED);
smb347_set_writable(smb, false);
smb347_set_writable(smb, false, false);
if (ret < 0) {
dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret);
......@@ -1241,6 +1270,13 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb)
/* Select charging control */
device_property_read_u32(dev, "summit,enable-charge-control",
&smb->enable_control);
/*
* Polarity of INOK signal indicating presence of external power
* supply connected to the charger.
*/
device_property_read_u32(dev, "summit,inok-polarity",
&smb->inok_polarity);
}
static int smb347_get_battery_info(struct smb347_charger *smb)
......@@ -1292,12 +1328,176 @@ static int smb347_get_battery_info(struct smb347_charger *smb)
return 0;
}
static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev)
{
struct smb347_charger *smb = rdev_get_drvdata(rdev);
unsigned int val;
int ret;
ret = regmap_read(smb->regmap, CFG_OTG, &val);
if (ret < 0)
return ret;
/*
* It's unknown what happens if this bit is unset due to lack of
* access to the datasheet, assume it's limit-enable.
*/
if (!(val & CFG_OTG_CURRENT_LIMIT_250mA))
return 0;
return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000;
}
static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb,
int max_uA)
{
const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA |
CFG_OTG_CURRENT_LIMIT_250mA;
unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA;
int ret;
if (max_uA >= 750000)
val |= CFG_OTG_CURRENT_LIMIT_750mA;
ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val);
if (ret < 0)
dev_err(smb->dev, "failed to change USB current limit\n");
return ret;
}
static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
struct smb347_charger *smb = rdev_get_drvdata(rdev);
int ret;
ret = smb347_set_writable(smb, true, true);
if (ret < 0)
return ret;
ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
smb347_set_writable(smb, false, true);
return ret;
}
static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev)
{
struct smb347_charger *smb = rdev_get_drvdata(rdev);
int ret, max_uA;
ret = smb347_set_writable(smb, true, true);
if (ret < 0)
return ret;
smb347_charging_disable(smb);
if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
unsigned int sysok = 0;
if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW)
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
/*
* VBUS won't be powered if INOK is active, so we need to
* manually disable INOK on some platforms.
*/
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
if (ret < 0) {
dev_err(smb->dev, "failed to disable INOK\n");
goto done;
}
}
ret = smb347_usb_vbus_get_current_limit(rdev);
if (ret < 0) {
dev_err(smb->dev, "failed to get USB VBUS current limit\n");
goto done;
}
max_uA = ret;
ret = smb347_usb_vbus_set_new_current_limit(smb, 250000);
if (ret < 0) {
dev_err(smb->dev, "failed to preset USB VBUS current limit\n");
goto done;
}
ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
if (ret < 0) {
dev_err(smb->dev, "failed to enable USB VBUS\n");
goto done;
}
smb->usb_vbus_enabled = true;
ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
if (ret < 0) {
dev_err(smb->dev, "failed to restore USB VBUS current limit\n");
goto done;
}
done:
smb347_set_writable(smb, false, true);
return ret;
}
static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev)
{
struct smb347_charger *smb = rdev_get_drvdata(rdev);
int ret;
ret = smb347_set_writable(smb, true, true);
if (ret < 0)
return ret;
ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
if (ret < 0) {
dev_err(smb->dev, "failed to disable USB VBUS\n");
goto done;
}
smb->usb_vbus_enabled = false;
if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
unsigned int sysok = 0;
if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH)
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
if (ret < 0) {
dev_err(smb->dev, "failed to enable INOK\n");
goto done;
}
}
smb347_start_stop_charging(smb);
done:
smb347_set_writable(smb, false, true);
return ret;
}
static const struct regmap_config smb347_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SMB347_MAX_REGISTER,
.volatile_reg = smb347_volatile_reg,
.readable_reg = smb347_readable_reg,
.cache_type = REGCACHE_FLAT,
.num_reg_defaults_raw = SMB347_MAX_REGISTER,
};
static const struct regulator_ops smb347_usb_vbus_regulator_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = smb347_usb_vbus_regulator_enable,
.disable = smb347_usb_vbus_regulator_disable,
.get_current_limit = smb347_usb_vbus_get_current_limit,
.set_current_limit = smb347_usb_vbus_set_current_limit,
};
static const struct power_supply_desc smb347_mains_desc = {
......@@ -1316,10 +1516,24 @@ static const struct power_supply_desc smb347_usb_desc = {
.num_properties = ARRAY_SIZE(smb347_properties),
};
static const struct regulator_desc smb347_usb_vbus_regulator_desc = {
.name = "smb347-usb-vbus",
.of_match = of_match_ptr("usb-vbus"),
.ops = &smb347_usb_vbus_regulator_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = CMD_A,
.enable_mask = CMD_A_OTG_ENABLED,
.enable_val = CMD_A_OTG_ENABLED,
.fixed_uV = 5000000,
.n_voltages = 1,
};
static int smb347_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct power_supply_config mains_usb_cfg = {};
struct regulator_config usb_rdev_cfg = {};
struct device *dev = &client->dev;
struct smb347_charger *smb;
int ret;
......@@ -1367,6 +1581,18 @@ static int smb347_probe(struct i2c_client *client,
if (ret)
return ret;
usb_rdev_cfg.dev = dev;
usb_rdev_cfg.driver_data = smb;
usb_rdev_cfg.regmap = smb->regmap;
smb->usb_rdev = devm_regulator_register(dev,
&smb347_usb_vbus_regulator_desc,
&usb_rdev_cfg);
if (IS_ERR(smb->usb_rdev)) {
smb347_irq_disable(smb);
return PTR_ERR(smb->usb_rdev);
}
return 0;
}
......@@ -1374,11 +1600,17 @@ static int smb347_remove(struct i2c_client *client)
{
struct smb347_charger *smb = i2c_get_clientdata(client);
smb347_usb_vbus_regulator_disable(smb->usb_rdev);
smb347_irq_disable(smb);
return 0;
}
static void smb347_shutdown(struct i2c_client *client)
{
smb347_remove(client);
}
static const struct i2c_device_id smb347_id[] = {
{ "smb345", SMB345 },
{ "smb347", SMB347 },
......@@ -1402,6 +1634,7 @@ static struct i2c_driver smb347_driver = {
},
.probe = smb347_probe,
.remove = smb347_remove,
.shutdown = smb347_shutdown,
.id_table = smb347_id,
};
module_i2c_driver(smb347_driver);
......
......@@ -16,4 +16,8 @@
#define SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW 1
#define SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH 2
/* Polarity of INOK signal */
#define SMB3XX_SYSOK_INOK_ACTIVE_LOW 0
#define SMB3XX_SYSOK_INOK_ACTIVE_HIGH 1
#endif
......@@ -41,6 +41,8 @@ int linear_range_get_selector_low(const struct linear_range *r,
int linear_range_get_selector_high(const struct linear_range *r,
unsigned int val, unsigned int *selector,
bool *found);
void linear_range_get_selector_within(const struct linear_range *r,
unsigned int val, unsigned int *selector);
int linear_range_get_selector_low_array(const struct linear_range *r,
int ranges, unsigned int val,
unsigned int *selector, bool *found);
......
......@@ -4228,6 +4228,7 @@ enum ec_device_event {
EC_DEVICE_EVENT_TRACKPAD,
EC_DEVICE_EVENT_DSP,
EC_DEVICE_EVENT_WIFI,
EC_DEVICE_EVENT_WLC,
};
enum ec_device_event_param {
......@@ -5460,6 +5461,72 @@ struct ec_response_rollback_info {
/* Issue AP reset */
#define EC_CMD_AP_RESET 0x0125
/**
* Get the number of peripheral charge ports
*/
#define EC_CMD_PCHG_COUNT 0x0134
#define EC_PCHG_MAX_PORTS 8
struct ec_response_pchg_count {
uint8_t port_count;
} __ec_align1;
/**
* Get the status of a peripheral charge port
*/
#define EC_CMD_PCHG 0x0135
struct ec_params_pchg {
uint8_t port;
} __ec_align1;
struct ec_response_pchg {
uint32_t error; /* enum pchg_error */
uint8_t state; /* enum pchg_state state */
uint8_t battery_percentage;
uint8_t unused0;
uint8_t unused1;
/* Fields added in version 1 */
uint32_t fw_version;
uint32_t dropped_event_count;
} __ec_align2;
enum pchg_state {
/* Charger is reset and not initialized. */
PCHG_STATE_RESET = 0,
/* Charger is initialized or disabled. */
PCHG_STATE_INITIALIZED,
/* Charger is enabled and ready to detect a device. */
PCHG_STATE_ENABLED,
/* Device is in proximity. */
PCHG_STATE_DETECTED,
/* Device is being charged. */
PCHG_STATE_CHARGING,
/* Device is fully charged. It implies DETECTED (& not charging). */
PCHG_STATE_FULL,
/* In download (a.k.a. firmware update) mode */
PCHG_STATE_DOWNLOAD,
/* In download mode. Ready for receiving data. */
PCHG_STATE_DOWNLOADING,
/* Device is ready for data communication. */
PCHG_STATE_CONNECTED,
/* Put no more entry below */
PCHG_STATE_COUNT,
};
#define EC_PCHG_STATE_TEXT { \
[PCHG_STATE_RESET] = "RESET", \
[PCHG_STATE_INITIALIZED] = "INITIALIZED", \
[PCHG_STATE_ENABLED] = "ENABLED", \
[PCHG_STATE_DETECTED] = "DETECTED", \
[PCHG_STATE_CHARGING] = "CHARGING", \
[PCHG_STATE_FULL] = "FULL", \
[PCHG_STATE_DOWNLOAD] = "DOWNLOAD", \
[PCHG_STATE_DOWNLOADING] = "DOWNLOADING", \
[PCHG_STATE_CONNECTED] = "CONNECTED", \
}
/*****************************************************************************/
/* Voltage regulator controls */
......
......@@ -69,7 +69,7 @@ enum max17042_register {
MAX17042_RelaxCFG = 0x2A,
MAX17042_MiscCFG = 0x2B,
MAX17042_TGAIN = 0x2C,
MAx17042_TOFF = 0x2D,
MAX17042_TOFF = 0x2D,
MAX17042_CGAIN = 0x2E,
MAX17042_COFF = 0x2F,
......@@ -110,13 +110,14 @@ enum max17042_register {
MAX17042_VFSOC = 0xFF,
};
/* Registers specific to max17055 only */
enum max17055_register {
MAX17055_QRes = 0x0C,
MAX17055_RCell = 0x14,
MAX17055_TTF = 0x20,
MAX17055_V_empty = 0x3A,
MAX17055_TIMER = 0x3E,
MAX17055_DieTemp = 0x34,
MAX17055_USER_MEM = 0x40,
MAX17055_RGAIN = 0x42,
MAX17055_RGAIN = 0x43,
MAX17055_ConvgCfg = 0x49,
MAX17055_VFRemCap = 0x4A,
......@@ -155,13 +156,14 @@ enum max17055_register {
MAX17055_AtAvCap = 0xDF,
};
/* Registers specific to max17047/50 */
/* Registers specific to max17047/50/55 */
enum max17047_register {
MAX17047_QRTbl00 = 0x12,
MAX17047_FullSOCThr = 0x13,
MAX17047_QRTbl10 = 0x22,
MAX17047_QRTbl20 = 0x32,
MAX17047_V_empty = 0x3A,
MAX17047_TIMER = 0x3E,
MAX17047_QRTbl30 = 0x42,
};
......
......@@ -352,6 +352,7 @@ struct power_supply_resistance_temp_table {
*/
struct power_supply_battery_info {
unsigned int technology; /* from the enum above */
int energy_full_design_uwh; /* microWatt-hours */
int charge_full_design_uah; /* microAmp-hours */
int voltage_min_design_uv; /* microVolts */
......
......@@ -241,5 +241,36 @@ int linear_range_get_selector_high(const struct linear_range *r,
}
EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
/**
* linear_range_get_selector_within - return linear range selector for value
* @r: pointer to linear range where selector is looked from
* @val: value for which the selector is searched
* @selector: address where found selector value is updated
*
* Return selector for which range value is closest match for given
* input value. Value is matching if it is equal or lower than given
* value. But return maximum selector if given value is higher than
* maximum value.
*/
void linear_range_get_selector_within(const struct linear_range *r,
unsigned int val, unsigned int *selector)
{
if (r->min > val) {
*selector = r->min_sel;
return;
}
if (linear_range_get_max_value(r) < val) {
*selector = r->max_sel;
return;
}
if (r->step == 0)
*selector = r->min_sel;
else
*selector = (val - r->min) / r->step + r->min_sel;
}
EXPORT_SYMBOL_GPL(linear_range_get_selector_within);
MODULE_DESCRIPTION("linear-ranges helper");
MODULE_LICENSE("GPL");
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