mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 14:14:24 +08:00
power supply and reset changes for the v5.15 series
battery/charger related changes: - 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 changes: - tps65086-restart: new driver - linkstation-poweroff: support NETGEAR ReadyNAS Duo v2 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmEmUVwACgkQ2O7X88g7 +poFbA//XimqwjO0MR7xqmm2905l78L3L1cNn7vRPzfdPbcf/kKPg8Jrx8kTn1EK wKdbP4ZQJOIyCLIFcI6oURUaNHh485KXj4DFvT13AsbkPw+2xUv0Ha5p8J698QAG RPmkTNHk+0M/K+/Z7/GPb6t0B7uQi3cg7/aIZeFo26FYpIP5XekoxP1xoLfE9lO2 aSrSbDh8oIjOLFPc4nuzm0x4Bcg/MpbUG1nhTBdP8OD8xjuMSmDUnbEvLgQYv4oP 9PLbi4jxm0VSfFhdECCSZ+x7CO0+wqxLXWCoOGlzDQ1Y2OYp9nG+Xgsj46HGh38c 11WER/16J7AfzUg1zqfu7NKDOKWad+TsTvQgXVK4GAxDOxpOS8Hz7GuP0/nnZBLx PoDAb7ZBtb6QXJDEvCDWoo+yMcZRaULbefQCgR/ys6bWoL+B6wdowxfV/daFGpmx fAMDGoSGrmYJhbPkcfAVJrN196zd5EQzbB6pyOfAPu3lJiDz+M/DyPNYwkljagAx JzSK80cwtXk07lgZZvC8Z3MJruN5pqqvWX/TA8l7dHpReoguCM3hAPUJ9pdVoIa7 haavL0UzLwya9C2sK9hcys8EBim5thkXI6GsnpRxiztkXZh0LtsUP9Dydt2srGA6 Hl4BD/g23W9+zmjkAIAvgMwbBoZ2/SHkD7l3ZqG2N2j6LrQxpwc= =UcdD -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
4520dcbe0d
@ -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
|
||||
|
@ -0,0 +1,48 @@
|
||||
# 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:
|
||||
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)
|
||||
|
||||
static struct phy_device *phydev;
|
||||
#define MII_88E1318S_PHY_LED_POL_LED2 BIT(4)
|
||||
|
||||
static void mvphy_reg_intn(u16 data)
|
||||
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 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 @@ err:
|
||||
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;
|
||||
|
||||
|
98
drivers/power/reset/tps65086-restart.c
Normal file
98
drivers/power/reset/tps65086-restart.c
Normal file
@ -0,0 +1,98 @@
|
||||
// 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,210 +421,13 @@ 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
|
||||
* @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 {
|
||||
int critical;
|
||||
int low;
|
||||
int normal;
|
||||
int high;
|
||||
int full;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_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 {
|
||||
int usb_volt_max;
|
||||
int usb_curr_max;
|
||||
int ac_volt_max;
|
||||
int ac_curr_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_bm_data - abx500 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_now present battery temperature
|
||||
* @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
|
||||
* @abx500_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
|
||||
* @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)
|
||||
* @n_chg_out_curr number of elements in array chg_output_curr
|
||||
* @n_chg_in_curr number of elements in array chg_input_curr
|
||||
* @chg_output_curr charger output current level map
|
||||
* @chg_input_curr charger input current level map
|
||||
* @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 abx500_bm_data {
|
||||
int temp_under;
|
||||
int temp_low;
|
||||
int temp_high;
|
||||
int temp_over;
|
||||
int temp_now;
|
||||
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;
|
||||
bool auto_trig;
|
||||
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;
|
||||
int n_chg_out_curr;
|
||||
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;
|
||||
};
|
||||
|
||||
enum {
|
||||
NTC_EXTERNAL = 0,
|
||||
NTC_INTERNAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct 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 res_to_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batres_vs_temp - defines one point in a temp vs battery internal
|
||||
* resistance curve.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: battery internal reistance in mOhm
|
||||
*/
|
||||
struct batres_vs_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/* 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
|
||||
@ -661,6 +464,7 @@ struct ab8500_bm_charger_parameters {
|
||||
* @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_now present battery temperature
|
||||
* @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
|
||||
@ -669,9 +473,10 @@ struct ab8500_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
|
||||
* @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
|
||||
* @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
|
||||
@ -679,7 +484,11 @@ struct ab8500_bm_charger_parameters {
|
||||
* @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
|
||||
* @n_chg_out_curr number of elements in array chg_output_curr
|
||||
* @n_chg_in_curr number of elements in array chg_input_curr
|
||||
* @chg_output_curr charger output current level map
|
||||
* @chg_input_curr charger input current level map
|
||||
* @maxi maximization parameters
|
||||
* @cap_levels capacity in percent for the different capacity levels
|
||||
* @bat_type table of supported battery types
|
||||
* @chg_params charger parameters
|
||||
@ -690,6 +499,7 @@ struct ab8500_bm_data {
|
||||
int temp_low;
|
||||
int temp_high;
|
||||
int temp_over;
|
||||
int temp_now;
|
||||
int temp_interval_chg;
|
||||
int temp_interval_nochg;
|
||||
int main_safety_tmr_h;
|
||||
@ -700,7 +510,8 @@ struct ab8500_bm_data {
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
bool enable_overshoot;
|
||||
enum abx500_adc_therm adc_therm;
|
||||
bool auto_trig;
|
||||
enum ab8500_adc_therm adc_therm;
|
||||
int fg_res;
|
||||
int n_btypes;
|
||||
int batt_id;
|
||||
@ -708,13 +519,49 @@ struct ab8500_bm_data {
|
||||
int interval_not_charging;
|
||||
int temp_hysteresis;
|
||||
int gnd_lift_resistance;
|
||||
int n_chg_out_curr;
|
||||
int n_chg_in_curr;
|
||||
int *chg_output_curr;
|
||||
int *chg_input_curr;
|
||||
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;
|
||||
};
|
||||
|
||||
extern struct abx500_bm_data ab8500_bm_data;
|
||||
enum {
|
||||
NTC_EXTERNAL = 0,
|
||||
NTC_INTERNAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct 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 res_to_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batres_vs_temp - defines one point in a temp vs battery internal
|
||||
* resistance curve.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: battery internal reistance in mOhm
|
||||
*/
|
||||
struct batres_vs_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct ab8500_fg;
|
||||
|
||||
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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,9 +20,8 @@
|
||||
#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)
|
||||
@ -78,7 +78,6 @@
|
||||
#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"
|
||||
|
||||
/* 1.1mV per LSB expressed in uV */
|
||||
@ -87,6 +86,7 @@
|
||||
#define PROP_VOLT(a) ((a) * 1000)
|
||||
#define PROP_CURR(a) ((a) * 1000)
|
||||
|
||||
#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
|
||||
#define AXP288_FG_INTR_NUM 6
|
||||
enum {
|
||||
QWBTU_IRQ = 0,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
|
||||
return 0;
|
||||
|
||||
dev_dbg(info->dev, "Fuel Gauge updating register values...\n");
|
||||
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug);
|
||||
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->ocv = ret;
|
||||
|
||||
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 = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->fg_cc_mtr1 = ret;
|
||||
|
||||
static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
|
||||
{
|
||||
debugfs_remove(info->debug_file);
|
||||
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;
|
||||
}
|
||||
#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)
|
||||
{
|
||||
}
|
||||
#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 @@ not_full:
|
||||
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)
|
||||
|
386
drivers/power/supply/cros_peripheral_charger.c
Normal file
386
drivers/power/supply/cros_peripheral_charger.c
Normal file
@ -0,0 +1,386 @@
|
||||
// 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_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
|
||||
|
||||
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);
|
||||
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_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_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);
|
||||
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) ||
|
||||
(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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
867
drivers/power/supply/mt6360_charger.c
Normal file
867
drivers/power/supply/mt6360_charger.c
Normal file
@ -0,0 +1,867 @@
|
||||
// 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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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,
|
||||
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");
|
||||
|
Loading…
Reference in New Issue
Block a user