mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
power supply and reset changes for the v4.11 series
* New drivers - sbs-charger driver - max14656_charger_detector - axp20x_ac_power * New chip/feature support - axp20x_usb_power: add AXP223 support - tps65217: add usb charger support - qcom_smbb: support otg regulator - at91-reset: add samx7 support * Dropped drivers - intel_mid_battery (platform was dropped) * Fixes - at91-poweroff: avoid wearing off LPDDR memory - replace deprecated extcon API - lots of cleanup and style fixes - misc. minor functionality fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAliqc7UACgkQ2O7X88g7 +pqV9g//T96CM/O6qHx5VPOAYrHP9pG+iOXjqMQi0oHqW4ggPMulGCLaD7rKpN9I 2pxEJmw9e+F9VAbaY6yFIldOuv/tIo/6Kpnq0VgzHvJoDsJzCbs70tRd4xyPesGL icrI9HZgWEj8jIcKCoBDWjoMHDJEj5igirzw2Rzx3rRcXvoDSrW097vWQyQOv8uN jmGHAsP/J3KaRZsUc5eHfHnbMd750ZWUjANMWcZwfOBkxyKEsiotXqeZqjy5VXyc EzrAvZ8/jZS2CdwgRZUYzPY+awCJqEqbqOx9jitXpO9zrcnOBeArbZDps2tCQxBB 8Ect4UgdkQ5LdHKYZKu9GkChCSOoJdT9wiXsKBATvk+/y2607dtAcVvuSCP7Ogu/ DWZan2oFIx+F15moPHPGKq8bCjwRGR1xFt0ENNpSEInVEG6q/qjpPx8QQvs5YhI0 4cJrm/ZrrdN61znfGDqm+vFqL2BOjiLpxzvOOg8ouTZ80NcrZl8oiAFargoD+rGG KnhRqeWVSKyC9TB01hh2ThQC8F7UHmhw7A10gcmLEx8xApweR8hYGQYtjCQT9Uqs r3biIyWDmEFgmp1J0iM8VJyNc/qcl8dcCCJb0qqfFhpqDoL+QoHLQo3yqM/1osS6 Bs0OZT3QKkHgsQSuDe/knWSr1wLtk/EAhVAolWfH5LztPPsaSrY= =rRsU -----END PGP SIGNATURE----- Merge tag 'for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: "New drivers: - sbs-charger driver - max14656_charger_detector - axp20x_ac_power New chip/feature support" - axp20x_usb_power: add AXP223 support - tps65217: add usb charger support - qcom_smbb: support otg regulator - at91-reset: add samx7 support Dropped drivers: - intel_mid_battery (platform was dropped) Fixes: - at91-poweroff: avoid wearing off LPDDR memory - replace deprecated extcon API - lots of cleanup and style fixes - misc minor functionality fixes" * tag 'for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (84 commits) power: supply: add AC power supply driver for AXP20X and AXP22X PMICs dt-bindings: power: supply: add AXP20X/AXP22X AC power supply power: supply: axp20x_usb_power: use IIO channels when available power: supply: max14656: Export I2C and OF device ID as module aliases power: supply: bq2415x: check for NULL acpi_id to avoid null pointer dereference power: supply: bq24190_charger: Adjust formatting power: supply: bq24190_charger: Handle fault before status on interrupt power: supply: bq24190_charger: Don't read fault register outside irq_handle_thread() power: supply: bq24190_charger: Call power_supply_changed() for relevant component power: supply: bq24190_charger: Install irq_handler_thread() at end of probe() power: supply: bq24190_charger: Call set_mode_host() on pm_resume() power: supply: bq24190_charger: Fix irq trigger to IRQF_TRIGGER_FALLING power: supply: qcom_smbb: add regulator dependency power: reset: at91-reset: remove leftover platform_device_id power: reset: at91-reset: add samx7 support power: supply: max14656: fix platform_no_drv_owner.cocci warnings power: supply: pcf50633-charger: Compress return logic into one line. power: supply: ab8500_btemp: Compress return logic into one line. power: reset: at91-poweroff: timely shutdown LPDDR memories ARM: at91: define LPDDR types ...
This commit is contained in:
commit
c9b9f207b9
@ -0,0 +1,22 @@
|
||||
AXP20X and AXP22X PMICs' AC power supply
|
||||
|
||||
Required Properties:
|
||||
- compatible: One of:
|
||||
"x-powers,axp202-ac-power-supply"
|
||||
"x-powers,axp221-ac-power-supply"
|
||||
|
||||
This node is a subnode of the axp20x PMIC.
|
||||
|
||||
The AXP20X can read the current current and voltage supplied by AC by
|
||||
reading ADC channels from the AXP20X ADC.
|
||||
|
||||
The AXP22X is only able to tell if an AC power supply is present and
|
||||
usable.
|
||||
|
||||
Example:
|
||||
|
||||
&axp209 {
|
||||
ac_power_supply: ac-power-supply {
|
||||
compatible = "x-powers,axp202-ac-power-supply";
|
||||
};
|
||||
};
|
@ -3,6 +3,11 @@ AXP20x USB power supply
|
||||
Required Properties:
|
||||
-compatible: One of: "x-powers,axp202-usb-power-supply"
|
||||
"x-powers,axp221-usb-power-supply"
|
||||
"x-powers,axp223-usb-power-supply"
|
||||
|
||||
The AXP223 PMIC shares most of its behaviour with the AXP221 but has slight
|
||||
variations such as the former being able to set the VBUS power supply max
|
||||
current to 100mA, unlike the latter.
|
||||
|
||||
This node is a subnode of the axp20x PMIC.
|
||||
|
||||
|
36
Documentation/devicetree/bindings/power/supply/bq27xxx.txt
Normal file
36
Documentation/devicetree/bindings/power/supply/bq27xxx.txt
Normal file
@ -0,0 +1,36 @@
|
||||
Binding for TI BQ27XXX fuel gauge family
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain one of the following:
|
||||
* "ti,bq27200" - BQ27200
|
||||
* "ti,bq27210" - BQ27210
|
||||
* "ti,bq27500" - deprecated, use revision specific property below
|
||||
* "ti,bq27510" - deprecated, use revision specific property below
|
||||
* "ti,bq27520" - deprecated, use revision specific property below
|
||||
* "ti,bq27500-1" - BQ27500/1
|
||||
* "ti,bq27510g1" - BQ27510-g1
|
||||
* "ti,bq27510g2" - BQ27510-g2
|
||||
* "ti,bq27510g3" - BQ27510-g3
|
||||
* "ti,bq27520g1" - BQ27520-g1
|
||||
* "ti,bq27520g2" - BQ27520-g2
|
||||
* "ti,bq27520g3" - BQ27520-g3
|
||||
* "ti,bq27520g4" - BQ27520-g4
|
||||
* "ti,bq27530" - BQ27530
|
||||
* "ti,bq27531" - BQ27531
|
||||
* "ti,bq27541" - BQ27541
|
||||
* "ti,bq27542" - BQ27542
|
||||
* "ti,bq27546" - BQ27546
|
||||
* "ti,bq27742" - BQ27742
|
||||
* "ti,bq27545" - BQ27545
|
||||
* "ti,bq27421" - BQ27421
|
||||
* "ti,bq27425" - BQ27425
|
||||
* "ti,bq27441" - BQ27441
|
||||
* "ti,bq27621" - BQ27621
|
||||
- reg: integer, i2c address of the device.
|
||||
|
||||
Example:
|
||||
|
||||
bq27510g3 {
|
||||
compatible = "ti,bq27510g3";
|
||||
reg = <0x55>;
|
||||
};
|
@ -105,6 +105,22 @@ PROPERTIES
|
||||
regulation must be done externally to fully comply with
|
||||
the JEITA safety guidelines if this flag is set.
|
||||
|
||||
- usb_otg_in-supply:
|
||||
Usage: optional
|
||||
Value type: <phandle>
|
||||
Description: Reference to the regulator supplying power to the USB_OTG_IN
|
||||
pin.
|
||||
|
||||
child nodes:
|
||||
- otg-vbus:
|
||||
Usage: optional
|
||||
Description: This node defines a regulator used to control the direction
|
||||
of VBUS voltage - specifically: whether to supply voltage
|
||||
to VBUS for host mode operation of the OTG port, or allow
|
||||
input voltage from external VBUS for charging. In the
|
||||
hardware, the supply for this regulator comes from
|
||||
usb_otg_in-supply.
|
||||
|
||||
EXAMPLE
|
||||
charger@1000 {
|
||||
compatible = "qcom,pm8941-charger";
|
||||
@ -128,4 +144,7 @@ charger@1000 {
|
||||
|
||||
qcom,fast-charge-current-limit = <1000000>;
|
||||
qcom,dc-charge-current-limit = <1000000>;
|
||||
usb_otg_in-supply = <&pm8941_5vs1>;
|
||||
|
||||
otg-vbus {};
|
||||
};
|
||||
|
@ -0,0 +1,23 @@
|
||||
SBS sbs-charger
|
||||
~~~~~~~~~~
|
||||
|
||||
Required properties:
|
||||
- compatible: "<vendor>,<part-number>", "sbs,sbs-charger" as fallback. The part
|
||||
number compatible string might be used in order to take care of vendor
|
||||
specific registers.
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: Should be the phandle for the interrupt controller. Use in
|
||||
conjunction with "interrupts".
|
||||
- interrupts: Interrupt mapping for GPIO IRQ. Use in conjunction with
|
||||
"interrupt-parent". If an interrupt is not provided the driver will switch
|
||||
automatically to polling.
|
||||
|
||||
Example:
|
||||
|
||||
ltc4100@9 {
|
||||
compatible = "lltc,ltc4100", "sbs,sbs-charger";
|
||||
reg = <0x9>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
@ -8,8 +8,10 @@ Optional properties :
|
||||
- interrupts : Specify the interrupt to be used to trigger when the AC
|
||||
adapter is either plugged in or removed.
|
||||
- ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter
|
||||
presence. This is a Host GPIO that is configured as an input and
|
||||
connected to the bq24735.
|
||||
status. This is a Host GPIO that is configured as an input and connected
|
||||
to the ACOK pin on the bq24735. Note: for backwards compatibility reasons,
|
||||
the GPIO must be active on AC adapter absence despite ACOK being active
|
||||
(high) on AC adapter presence.
|
||||
- ti,charge-current : Used to control and set the charging current. This value
|
||||
must be between 128mA and 8.128A with a 64mA step resolution. The POR value
|
||||
is 0x0000h. This number is in mA (e.g. 8192), see spec for more information
|
||||
@ -25,6 +27,8 @@ Optional properties :
|
||||
- ti,external-control : Indicates that the charger is configured externally
|
||||
and that the host should not attempt to enable/disable charging or set the
|
||||
charge voltage/current.
|
||||
- poll-interval : In case 'interrupts' is not specified, poll AC adapter
|
||||
presence with this interval (milliseconds).
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
Maxim MAX14656 / AL32 USB Charger Detector
|
||||
|
||||
Required properties :
|
||||
- compatible : "maxim,max14656";
|
||||
- reg: i2c slave address
|
||||
- interrupt-parent: the phandle for the interrupt controller
|
||||
- interrupts: interrupt line
|
||||
|
||||
Example:
|
||||
|
||||
&i2c2 {
|
||||
clock-frequency = <50000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c2>;
|
||||
status = "okay";
|
||||
|
||||
max14656@35 {
|
||||
compatible = "maxim,max14656";
|
||||
reg = <0x35>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_charger_detect>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <26 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
@ -32,7 +32,7 @@ config POWER_RESET_AT91_RESET
|
||||
|
||||
config POWER_RESET_AT91_SAMA5D2_SHDWC
|
||||
tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on ARCH_AT91
|
||||
default SOC_SAMA5
|
||||
help
|
||||
This driver supports the alternate shutdown controller for some Atmel
|
||||
|
@ -14,9 +14,12 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#include <soc/at91/at91sam9_ddrsdr.h>
|
||||
|
||||
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
||||
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
|
||||
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
|
||||
@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {
|
||||
|
||||
static void __iomem *at91_shdwc_base;
|
||||
static struct clk *sclk;
|
||||
static void __iomem *mpddrc_base;
|
||||
|
||||
static void __init at91_wakeup_status(void)
|
||||
{
|
||||
@ -73,6 +77,29 @@ static void at91_poweroff(void)
|
||||
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
|
||||
}
|
||||
|
||||
static void at91_lpddr_poweroff(void)
|
||||
{
|
||||
asm volatile(
|
||||
/* Align to cache lines */
|
||||
".balign 32\n\t"
|
||||
|
||||
/* Ensure AT91_SHDW_CR is in the TLB by reading it */
|
||||
" ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
|
||||
|
||||
/* Power down SDRAM0 */
|
||||
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
|
||||
/* Shutdown CPU */
|
||||
" str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
|
||||
|
||||
" b .\n\t"
|
||||
:
|
||||
: "r" (mpddrc_base),
|
||||
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
|
||||
"r" (at91_shdwc_base),
|
||||
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
|
||||
: "r0");
|
||||
}
|
||||
|
||||
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
|
||||
{
|
||||
const char *pm;
|
||||
@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
|
||||
static int __init at91_poweroff_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device_node *np;
|
||||
u32 ddr_type;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -150,12 +179,30 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
|
||||
|
||||
pm_power_off = at91_poweroff;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
mpddrc_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
|
||||
if (!mpddrc_base)
|
||||
return 0;
|
||||
|
||||
ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
|
||||
if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
|
||||
(ddr_type == AT91_DDRSDRC_MD_LPDDR3))
|
||||
pm_power_off = at91_lpddr_poweroff;
|
||||
else
|
||||
iounmap(mpddrc_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit at91_poweroff_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (pm_power_off == at91_poweroff)
|
||||
if (pm_power_off == at91_poweroff ||
|
||||
pm_power_off == at91_lpddr_poweroff)
|
||||
pm_power_off = NULL;
|
||||
|
||||
clk_disable_unprepare(sclk);
|
||||
@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id at91_ramc_of_match[] = {
|
||||
{ .compatible = "atmel,sama5d3-ddramc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct of_device_id at91_poweroff_of_match[] = {
|
||||
{ .compatible = "atmel,at91sam9260-shdwc", },
|
||||
{ .compatible = "atmel,at91sam9rl-shdwc", },
|
||||
|
@ -134,6 +134,15 @@ static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int samx7_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PROCRST),
|
||||
at91_rstc_base);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void __init at91_reset_status(struct platform_device *pdev)
|
||||
{
|
||||
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
|
||||
@ -173,6 +182,7 @@ static const struct of_device_id at91_reset_of_match[] = {
|
||||
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
|
||||
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
|
||||
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
|
||||
{ .compatible = "atmel,samx7-rstc", .data = samx7_restart },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
|
||||
@ -238,20 +248,12 @@ static int __exit at91_reset_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id at91_reset_plat_match[] = {
|
||||
{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
|
||||
{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, at91_reset_plat_match);
|
||||
|
||||
static struct platform_driver at91_reset_driver = {
|
||||
.remove = __exit_p(at91_reset_remove),
|
||||
.driver = {
|
||||
.name = "at91-reset",
|
||||
.of_match_table = at91_reset_of_match,
|
||||
},
|
||||
.id_table = at91_reset_plat_match,
|
||||
};
|
||||
module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
|
||||
|
||||
|
@ -22,9 +22,12 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#include <soc/at91/at91sam9_ddrsdr.h>
|
||||
|
||||
#define SLOW_CLOCK_FREQ 32768
|
||||
|
||||
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
|
||||
@ -75,6 +78,7 @@ struct shdwc {
|
||||
*/
|
||||
static struct shdwc *at91_shdwc;
|
||||
static struct clk *sclk;
|
||||
static void __iomem *mpddrc_base;
|
||||
|
||||
static const unsigned long long sdwc_dbc_period[] = {
|
||||
0, 3, 32, 512, 4096, 32768,
|
||||
@ -108,6 +112,29 @@ static void at91_poweroff(void)
|
||||
at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);
|
||||
}
|
||||
|
||||
static void at91_lpddr_poweroff(void)
|
||||
{
|
||||
asm volatile(
|
||||
/* Align to cache lines */
|
||||
".balign 32\n\t"
|
||||
|
||||
/* Ensure AT91_SHDW_CR is in the TLB by reading it */
|
||||
" ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
|
||||
|
||||
/* Power down SDRAM0 */
|
||||
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
|
||||
/* Shutdown CPU */
|
||||
" str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
|
||||
|
||||
" b .\n\t"
|
||||
:
|
||||
: "r" (mpddrc_base),
|
||||
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
|
||||
"r" (at91_shdwc->at91_shdwc_base),
|
||||
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
|
||||
: "r0");
|
||||
}
|
||||
|
||||
static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
|
||||
u32 in_period_us)
|
||||
{
|
||||
@ -212,6 +239,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np;
|
||||
u32 ddr_type;
|
||||
int ret;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
@ -249,6 +278,23 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
||||
|
||||
pm_power_off = at91_poweroff;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
mpddrc_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
|
||||
if (!mpddrc_base)
|
||||
return 0;
|
||||
|
||||
ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
|
||||
if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
|
||||
(ddr_type == AT91_DDRSDRC_MD_LPDDR3))
|
||||
pm_power_off = at91_lpddr_poweroff;
|
||||
else
|
||||
iounmap(mpddrc_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -256,7 +302,8 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct shdwc *shdw = platform_get_drvdata(pdev);
|
||||
|
||||
if (pm_power_off == at91_poweroff)
|
||||
if (pm_power_off == at91_poweroff ||
|
||||
pm_power_off == at91_lpddr_poweroff)
|
||||
pm_power_off = NULL;
|
||||
|
||||
/* Reset values to disable wake-up features */
|
||||
|
@ -164,6 +164,12 @@ config BATTERY_SBS
|
||||
Say Y to include support for SBS battery driver for SBS-compliant
|
||||
gas gauges.
|
||||
|
||||
config CHARGER_SBS
|
||||
tristate "SBS Compliant charger"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to include support for SBS compilant battery chargers.
|
||||
|
||||
config BATTERY_BQ27XXX
|
||||
tristate "BQ27xxx battery driver"
|
||||
help
|
||||
@ -214,6 +220,18 @@ config BATTERY_DA9150
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called da9150-fg.
|
||||
|
||||
config CHARGER_AXP20X
|
||||
tristate "X-Powers AXP20X and AXP22X AC power supply driver"
|
||||
depends on MFD_AXP20X
|
||||
depends on AXP20X_ADC
|
||||
depends on IIO
|
||||
help
|
||||
Say Y here to enable support for X-Powers AXP20X and AXP22X PMICs' AC
|
||||
power supply.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called axp20x_ac_power.
|
||||
|
||||
config AXP288_CHARGER
|
||||
tristate "X-Powers AXP288 Charger"
|
||||
depends on MFD_AXP20X && EXTCON_AXP288
|
||||
@ -292,13 +310,6 @@ config BATTERY_JZ4740
|
||||
This driver can be build as a module. If so, the module will be
|
||||
called jz4740-battery.
|
||||
|
||||
config BATTERY_INTEL_MID
|
||||
tristate "Battery driver for Intel MID platforms"
|
||||
depends on INTEL_SCU_IPC && SPI
|
||||
help
|
||||
Say Y here to enable the battery driver on Intel MID
|
||||
platforms.
|
||||
|
||||
config BATTERY_RX51
|
||||
tristate "Nokia RX-51 (N900) battery driver"
|
||||
depends on TWL4030_MADC
|
||||
@ -370,6 +381,16 @@ config CHARGER_MAX14577
|
||||
Say Y to enable support for the battery charger control sysfs and
|
||||
platform data of MAX14577/77836 MUICs.
|
||||
|
||||
config CHARGER_DETECTOR_MAX14656
|
||||
tristate "Maxim MAX14656 USB charger detector"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
help
|
||||
Say Y to enable support for the Maxim MAX14656 USB charger detector.
|
||||
The device is compliant with the USB Battery Charging Specification
|
||||
Revision 1.2 and can be found e.g. in Kindle 4/5th generation
|
||||
readers and certain LG devices.
|
||||
|
||||
config CHARGER_MAX77693
|
||||
tristate "Maxim MAX77693 battery charger driver"
|
||||
depends on MFD_MAX77693
|
||||
@ -395,6 +416,7 @@ config CHARGER_QCOM_SMBB
|
||||
depends on MFD_SPMI_PMIC || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on EXTCON
|
||||
depends on REGULATOR
|
||||
help
|
||||
Say Y to include support for the Switch-Mode Battery Charger and
|
||||
Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o
|
||||
|
||||
obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
|
||||
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
|
||||
obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
|
||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
|
||||
@ -31,6 +32,7 @@ obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
|
||||
obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
|
||||
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
||||
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
|
||||
obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o
|
||||
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
|
||||
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
|
||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
||||
@ -47,7 +49,6 @@ 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_JZ4740) += jz4740-battery.o
|
||||
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.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 pm2301_charger.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
@ -58,6 +59,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
|
||||
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
|
||||
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
||||
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
|
||||
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
|
||||
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
|
@ -76,8 +76,8 @@ struct ab8500_btemp_ranges {
|
||||
* @dev: Pointer to the structure device
|
||||
* @node: List of AB8500 BTEMPs, hence prepared for reentrance
|
||||
* @curr_source: What current source we use, in uA
|
||||
* @bat_temp: Dispatched battery temperature in degree Celcius
|
||||
* @prev_bat_temp Last measured battery temperature in degree Celcius
|
||||
* @bat_temp: Dispatched battery temperature in degree Celsius
|
||||
* @prev_bat_temp Last measured battery temperature in degree Celsius
|
||||
* @parent: Pointer to the struct ab8500
|
||||
* @gpadc: Pointer to the struct gpadc
|
||||
* @fg: Pointer to the struct fg
|
||||
@ -123,10 +123,7 @@ static LIST_HEAD(ab8500_btemp_list);
|
||||
*/
|
||||
struct ab8500_btemp *ab8500_btemp_get(void)
|
||||
{
|
||||
struct ab8500_btemp *btemp;
|
||||
btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
|
||||
|
||||
return btemp;
|
||||
return list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_btemp_get);
|
||||
|
||||
@ -464,13 +461,13 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
|
||||
* @tbl_size: size of the resistance to temperature table
|
||||
* @res: resistance to calculate the temperature from
|
||||
*
|
||||
* This function returns the battery temperature in degrees Celcius
|
||||
* This function returns the battery temperature in degrees Celsius
|
||||
* 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)
|
||||
{
|
||||
int i, temp;
|
||||
int i;
|
||||
/*
|
||||
* Calculate the formula for the straight line
|
||||
* Simple interpolation if we are within
|
||||
@ -488,9 +485,8 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
||||
i++;
|
||||
}
|
||||
|
||||
temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
|
||||
return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
|
||||
(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
253
drivers/power/supply/axp20x_ac_power.c
Normal file
253
drivers/power/supply/axp20x_ac_power.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* AXP20X and AXP22X PMICs' ACIN power supply driver
|
||||
*
|
||||
* Copyright (C) 2016 Free Electrons
|
||||
* Quentin Schulz <quentin.schulz@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
#define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
|
||||
#define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6)
|
||||
|
||||
#define DRVNAME "axp20x-ac-power-supply"
|
||||
|
||||
struct axp20x_ac_power {
|
||||
struct regmap *regmap;
|
||||
struct power_supply *supply;
|
||||
struct iio_channel *acin_v;
|
||||
struct iio_channel *acin_i;
|
||||
};
|
||||
|
||||
static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
|
||||
{
|
||||
struct axp20x_ac_power *power = devid;
|
||||
|
||||
power_supply_changed(power->supply);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int axp20x_ac_power_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
|
||||
int ret, reg;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
return 0;
|
||||
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
|
||||
return 0;
|
||||
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
|
||||
return 0;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = iio_read_channel_processed(power->acin_v, &val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IIO framework gives mV but Power Supply framework gives uV */
|
||||
val->intval *= 1000;
|
||||
|
||||
return 0;
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = iio_read_channel_processed(power->acin_i, &val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IIO framework gives mA but Power Supply framework gives uA */
|
||||
val->intval *= 1000;
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static enum power_supply_property axp20x_ac_power_properties[] = {
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
};
|
||||
|
||||
static enum power_supply_property axp22x_ac_power_properties[] = {
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc axp20x_ac_power_desc = {
|
||||
.name = "axp20x-ac",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.properties = axp20x_ac_power_properties,
|
||||
.num_properties = ARRAY_SIZE(axp20x_ac_power_properties),
|
||||
.get_property = axp20x_ac_power_get_property,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc axp22x_ac_power_desc = {
|
||||
.name = "axp22x-ac",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.properties = axp22x_ac_power_properties,
|
||||
.num_properties = ARRAY_SIZE(axp22x_ac_power_properties),
|
||||
.get_property = axp20x_ac_power_get_property,
|
||||
};
|
||||
|
||||
struct axp_data {
|
||||
const struct power_supply_desc *power_desc;
|
||||
bool acin_adc;
|
||||
};
|
||||
|
||||
static const struct axp_data axp20x_data = {
|
||||
.power_desc = &axp20x_ac_power_desc,
|
||||
.acin_adc = true,
|
||||
};
|
||||
|
||||
static const struct axp_data axp22x_data = {
|
||||
.power_desc = &axp22x_ac_power_desc,
|
||||
.acin_adc = false,
|
||||
};
|
||||
|
||||
static int axp20x_ac_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct axp20x_ac_power *power;
|
||||
struct axp_data *axp_data;
|
||||
static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
|
||||
NULL };
|
||||
int i, irq, ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
return -ENODEV;
|
||||
|
||||
if (!axp20x) {
|
||||
dev_err(&pdev->dev, "Parent drvdata not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
|
||||
if (!power)
|
||||
return -ENOMEM;
|
||||
|
||||
axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (axp_data->acin_adc) {
|
||||
power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
|
||||
if (IS_ERR(power->acin_v)) {
|
||||
if (PTR_ERR(power->acin_v) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(power->acin_v);
|
||||
}
|
||||
|
||||
power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i");
|
||||
if (IS_ERR(power->acin_i)) {
|
||||
if (PTR_ERR(power->acin_i) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(power->acin_i);
|
||||
}
|
||||
}
|
||||
|
||||
power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
|
||||
platform_set_drvdata(pdev, power);
|
||||
|
||||
psy_cfg.of_node = pdev->dev.of_node;
|
||||
psy_cfg.drv_data = power;
|
||||
|
||||
power->supply = devm_power_supply_register(&pdev->dev,
|
||||
axp_data->power_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(power->supply))
|
||||
return PTR_ERR(power->supply);
|
||||
|
||||
/* Request irqs after registering, as irqs may trigger immediately */
|
||||
for (i = 0; irq_names[i]; i++) {
|
||||
irq = platform_get_irq_byname(pdev, irq_names[i]);
|
||||
if (irq < 0) {
|
||||
dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
|
||||
irq_names[i], irq);
|
||||
continue;
|
||||
}
|
||||
irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
|
||||
ret = devm_request_any_context_irq(&pdev->dev, irq,
|
||||
axp20x_ac_power_irq, 0,
|
||||
DRVNAME, power);
|
||||
if (ret < 0)
|
||||
dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
|
||||
irq_names[i], ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id axp20x_ac_power_match[] = {
|
||||
{
|
||||
.compatible = "x-powers,axp202-ac-power-supply",
|
||||
.data = (void *)&axp20x_data,
|
||||
}, {
|
||||
.compatible = "x-powers,axp221-ac-power-supply",
|
||||
.data = (void *)&axp22x_data,
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
|
||||
|
||||
static struct platform_driver axp20x_ac_power_driver = {
|
||||
.probe = axp20x_ac_power_probe,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_ac_power_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(axp20x_ac_power_driver);
|
||||
|
||||
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -17,10 +17,12 @@
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
#define DRVNAME "axp20x-usb-power-supply"
|
||||
|
||||
@ -30,6 +32,8 @@
|
||||
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
|
||||
|
||||
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
|
||||
#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
|
||||
#define AXP20X_VBUS_VHOLD_OFFSET 3
|
||||
#define AXP20X_VBUS_CLIMIT_MASK 3
|
||||
#define AXP20X_VBUC_CLIMIT_900mA 0
|
||||
#define AXP20X_VBUC_CLIMIT_500mA 1
|
||||
@ -45,6 +49,9 @@ struct axp20x_usb_power {
|
||||
struct device_node *np;
|
||||
struct regmap *regmap;
|
||||
struct power_supply *supply;
|
||||
enum axp20x_variants axp20x_id;
|
||||
struct iio_channel *vbus_v;
|
||||
struct iio_channel *vbus_i;
|
||||
};
|
||||
|
||||
static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
|
||||
@ -72,6 +79,20 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
val->intval = AXP20X_VBUS_VHOLD_uV(v);
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
|
||||
ret = iio_read_channel_processed(power->vbus_v,
|
||||
&val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* IIO framework gives mV but Power Supply framework
|
||||
* gives uV.
|
||||
*/
|
||||
val->intval *= 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = axp20x_read_variable_width(power->regmap,
|
||||
AXP20X_VBUS_V_ADC_H, 12);
|
||||
if (ret < 0)
|
||||
@ -86,12 +107,10 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
|
||||
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
|
||||
case AXP20X_VBUC_CLIMIT_100mA:
|
||||
if (of_device_is_compatible(power->np,
|
||||
"x-powers,axp202-usb-power-supply")) {
|
||||
val->intval = 100000;
|
||||
} else {
|
||||
if (power->axp20x_id == AXP221_ID)
|
||||
val->intval = -1; /* No 100mA limit */
|
||||
}
|
||||
else
|
||||
val->intval = 100000;
|
||||
break;
|
||||
case AXP20X_VBUC_CLIMIT_500mA:
|
||||
val->intval = 500000;
|
||||
@ -105,6 +124,20 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
}
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
|
||||
ret = iio_read_channel_processed(power->vbus_i,
|
||||
&val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* IIO framework gives mA but Power Supply framework
|
||||
* gives uA.
|
||||
*/
|
||||
val->intval *= 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = axp20x_read_variable_width(power->regmap,
|
||||
AXP20X_VBUS_I_ADC_H, 12);
|
||||
if (ret < 0)
|
||||
@ -130,8 +163,7 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
if (of_device_is_compatible(power->np,
|
||||
"x-powers,axp202-usb-power-supply")) {
|
||||
if (power->axp20x_id == AXP202_ID) {
|
||||
ret = regmap_read(power->regmap,
|
||||
AXP20X_USB_OTG_STATUS, &v);
|
||||
if (ret)
|
||||
@ -155,6 +187,81 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
|
||||
int intval)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (intval) {
|
||||
case 4000000:
|
||||
case 4100000:
|
||||
case 4200000:
|
||||
case 4300000:
|
||||
case 4400000:
|
||||
case 4500000:
|
||||
case 4600000:
|
||||
case 4700000:
|
||||
val = (intval - 4000000) / 100000;
|
||||
return regmap_update_bits(power->regmap,
|
||||
AXP20X_VBUS_IPSOUT_MGMT,
|
||||
AXP20X_VBUS_VHOLD_MASK,
|
||||
val << AXP20X_VBUS_VHOLD_OFFSET);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power,
|
||||
int intval)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (intval) {
|
||||
case 100000:
|
||||
if (power->axp20x_id == AXP221_ID)
|
||||
return -EINVAL;
|
||||
case 500000:
|
||||
case 900000:
|
||||
val = (900000 - intval) / 400000;
|
||||
return regmap_update_bits(power->regmap,
|
||||
AXP20X_VBUS_IPSOUT_MGMT,
|
||||
AXP20X_VBUS_CLIMIT_MASK, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||
return axp20x_usb_power_set_voltage_min(power, val->intval);
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
return axp20x_usb_power_set_current_max(power, val->intval);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
|
||||
psp == POWER_SUPPLY_PROP_CURRENT_MAX;
|
||||
}
|
||||
|
||||
static enum power_supply_property axp20x_usb_power_properties[] = {
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
@ -178,7 +285,9 @@ static const struct power_supply_desc axp20x_usb_power_desc = {
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = axp20x_usb_power_properties,
|
||||
.num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
|
||||
.property_is_writeable = axp20x_usb_power_prop_writeable,
|
||||
.get_property = axp20x_usb_power_get_property,
|
||||
.set_property = axp20x_usb_power_set_property,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc axp22x_usb_power_desc = {
|
||||
@ -186,9 +295,41 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = axp22x_usb_power_properties,
|
||||
.num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
|
||||
.property_is_writeable = axp20x_usb_power_prop_writeable,
|
||||
.get_property = axp20x_usb_power_get_property,
|
||||
.set_property = axp20x_usb_power_set_property,
|
||||
};
|
||||
|
||||
static int configure_iio_channels(struct platform_device *pdev,
|
||||
struct axp20x_usb_power *power)
|
||||
{
|
||||
power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
|
||||
if (IS_ERR(power->vbus_v)) {
|
||||
if (PTR_ERR(power->vbus_v) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(power->vbus_v);
|
||||
}
|
||||
|
||||
power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
|
||||
if (IS_ERR(power->vbus_i)) {
|
||||
if (PTR_ERR(power->vbus_i) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(power->vbus_i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int configure_adc_registers(struct axp20x_usb_power *power)
|
||||
{
|
||||
/* Enable vbus voltage and current measurement */
|
||||
return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
|
||||
AXP20X_ADC_EN1_VBUS_CURR |
|
||||
AXP20X_ADC_EN1_VBUS_VOLT,
|
||||
AXP20X_ADC_EN1_VBUS_CURR |
|
||||
AXP20X_ADC_EN1_VBUS_VOLT);
|
||||
}
|
||||
|
||||
static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -214,11 +355,13 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
if (!power)
|
||||
return -ENOMEM;
|
||||
|
||||
power->axp20x_id = (enum axp20x_variants)of_device_get_match_data(
|
||||
&pdev->dev);
|
||||
|
||||
power->np = pdev->dev.of_node;
|
||||
power->regmap = axp20x->regmap;
|
||||
|
||||
if (of_device_is_compatible(power->np,
|
||||
"x-powers,axp202-usb-power-supply")) {
|
||||
if (power->axp20x_id == AXP202_ID) {
|
||||
/* Enable vbus valid checking */
|
||||
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
|
||||
AXP20X_VBUS_MON_VBUS_VALID,
|
||||
@ -226,17 +369,18 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable vbus voltage and current measurement */
|
||||
ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
|
||||
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
|
||||
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
|
||||
if (IS_ENABLED(CONFIG_AXP20X_ADC))
|
||||
ret = configure_iio_channels(pdev, power);
|
||||
else
|
||||
ret = configure_adc_registers(power);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usb_power_desc = &axp20x_usb_power_desc;
|
||||
irq_names = axp20x_irq_names;
|
||||
} else if (of_device_is_compatible(power->np,
|
||||
"x-powers,axp221-usb-power-supply")) {
|
||||
} else if (power->axp20x_id == AXP221_ID ||
|
||||
power->axp20x_id == AXP223_ID) {
|
||||
usb_power_desc = &axp22x_usb_power_desc;
|
||||
irq_names = axp22x_irq_names;
|
||||
} else {
|
||||
@ -273,9 +417,16 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id axp20x_usb_power_match[] = {
|
||||
{ .compatible = "x-powers,axp202-usb-power-supply" },
|
||||
{ .compatible = "x-powers,axp221-usb-power-supply" },
|
||||
{ }
|
||||
{
|
||||
.compatible = "x-powers,axp202-usb-power-supply",
|
||||
.data = (void *)AXP202_ID,
|
||||
}, {
|
||||
.compatible = "x-powers,axp221-usb-power-supply",
|
||||
.data = (void *)AXP221_ID,
|
||||
}, {
|
||||
.compatible = "x-powers,axp223-usb-power-supply",
|
||||
.data = (void *)AXP223_ID,
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
|
||||
|
||||
|
@ -90,20 +90,6 @@
|
||||
#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
|
||||
#define CHRG_VHTFC_45C 0x1F /* 45 DegC */
|
||||
|
||||
#define BAT_IRQ_CFG_CHRG_DONE (1 << 2)
|
||||
#define BAT_IRQ_CFG_CHRG_START (1 << 3)
|
||||
#define BAT_IRQ_CFG_BAT_SAFE_EXIT (1 << 4)
|
||||
#define BAT_IRQ_CFG_BAT_SAFE_ENTER (1 << 5)
|
||||
#define BAT_IRQ_CFG_BAT_DISCON (1 << 6)
|
||||
#define BAT_IRQ_CFG_BAT_CONN (1 << 7)
|
||||
#define BAT_IRQ_CFG_BAT_MASK 0xFC
|
||||
|
||||
#define TEMP_IRQ_CFG_QCBTU (1 << 4)
|
||||
#define TEMP_IRQ_CFG_CBTU (1 << 5)
|
||||
#define TEMP_IRQ_CFG_QCBTO (1 << 6)
|
||||
#define TEMP_IRQ_CFG_CBTO (1 << 7)
|
||||
#define TEMP_IRQ_CFG_MASK 0xF0
|
||||
|
||||
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
|
||||
|
||||
#define CV_4100MV 4100 /* 4100mV */
|
||||
@ -127,6 +113,10 @@
|
||||
#define ILIM_3000MA 3000 /* 3000mA */
|
||||
|
||||
#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
|
||||
#define USB_HOST_EXTCON_DEV_NAME "INT3496:00"
|
||||
|
||||
static const unsigned int cable_ids[] =
|
||||
{ EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP };
|
||||
|
||||
enum {
|
||||
VBUS_OV_IRQ = 0,
|
||||
@ -143,7 +133,6 @@ enum {
|
||||
|
||||
struct axp288_chrg_info {
|
||||
struct platform_device *pdev;
|
||||
struct axp20x_chrg_pdata *pdata;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
int irq[CHRG_INTR_END];
|
||||
@ -163,20 +152,16 @@ struct axp288_chrg_info {
|
||||
struct extcon_dev *edev;
|
||||
bool connected;
|
||||
enum power_supply_type chg_type;
|
||||
struct notifier_block nb;
|
||||
struct notifier_block nb[ARRAY_SIZE(cable_ids)];
|
||||
struct work_struct work;
|
||||
} cable;
|
||||
|
||||
int health;
|
||||
int inlmt;
|
||||
int cc;
|
||||
int cv;
|
||||
int max_cc;
|
||||
int max_cv;
|
||||
bool online;
|
||||
bool present;
|
||||
bool enable_charger;
|
||||
bool is_charger_enabled;
|
||||
int is_charger_enabled;
|
||||
};
|
||||
|
||||
static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
|
||||
@ -305,6 +290,9 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((int)enable == info->is_charger_enabled)
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
|
||||
CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
|
||||
@ -430,8 +418,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
||||
ret = axp288_charger_is_present(info);
|
||||
if (ret < 0)
|
||||
goto psy_get_prop_fail;
|
||||
info->present = ret;
|
||||
val->intval = info->present;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
/* Check for OTG case first */
|
||||
@ -442,8 +429,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
||||
ret = axp288_charger_is_online(info);
|
||||
if (ret < 0)
|
||||
goto psy_get_prop_fail;
|
||||
info->online = ret;
|
||||
val->intval = info->online;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = axp288_get_charger_health(info);
|
||||
@ -576,20 +562,20 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(work, struct axp288_chrg_info, cable.work);
|
||||
int ret, current_limit;
|
||||
bool changed = false;
|
||||
struct extcon_dev *edev = info->cable.edev;
|
||||
bool old_connected = info->cable.connected;
|
||||
enum power_supply_type old_chg_type = info->cable.chg_type;
|
||||
|
||||
/* Determine cable/charger type */
|
||||
if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) {
|
||||
if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
|
||||
} else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) {
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
|
||||
} else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) {
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
|
||||
@ -601,22 +587,15 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
|
||||
}
|
||||
|
||||
/* Cable status changed */
|
||||
if (old_connected != info->cable.connected)
|
||||
changed = true;
|
||||
|
||||
if (!changed)
|
||||
if (old_connected == info->cable.connected &&
|
||||
old_chg_type == info->cable.chg_type)
|
||||
return;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
if (info->is_charger_enabled && !info->cable.connected) {
|
||||
info->enable_charger = false;
|
||||
ret = axp288_charger_enable_charger(info, info->enable_charger);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev,
|
||||
"cannot disable charger (%d)", ret);
|
||||
if (info->cable.connected) {
|
||||
axp288_charger_enable_charger(info, false);
|
||||
|
||||
} else if (!info->is_charger_enabled && info->cable.connected) {
|
||||
switch (info->cable.chg_type) {
|
||||
case POWER_SUPPLY_TYPE_USB:
|
||||
current_limit = ILIM_500MA;
|
||||
@ -635,36 +614,49 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
|
||||
|
||||
/* Set vbus current limit first, then enable charger */
|
||||
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
|
||||
if (ret < 0) {
|
||||
if (ret == 0)
|
||||
axp288_charger_enable_charger(info, true);
|
||||
else
|
||||
dev_err(&info->pdev->dev,
|
||||
"error setting current limit (%d)", ret);
|
||||
} else {
|
||||
info->enable_charger = (current_limit > 0);
|
||||
ret = axp288_charger_enable_charger(info,
|
||||
info->enable_charger);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev,
|
||||
"cannot enable charger (%d)", ret);
|
||||
}
|
||||
} else {
|
||||
axp288_charger_enable_charger(info, false);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
info->health = axp288_get_charger_health(info);
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
if (changed)
|
||||
power_supply_changed(info->psy_usb);
|
||||
power_supply_changed(info->psy_usb);
|
||||
}
|
||||
|
||||
static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
/*
|
||||
* We need 3 copies of this, because there is no way to find out for which
|
||||
* cable id we are being called from the passed in arguments; and we must
|
||||
* have a separate nb for each extcon_register_notifier call.
|
||||
*/
|
||||
static int axp288_charger_handle_cable0_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb);
|
||||
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[0]);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int axp288_charger_handle_cable1_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[1]);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int axp288_charger_handle_cable2_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[2]);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
@ -672,7 +664,17 @@ static void axp288_charger_otg_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(work, struct axp288_chrg_info, otg.work);
|
||||
int ret;
|
||||
struct extcon_dev *edev = info->otg.cable;
|
||||
int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
|
||||
|
||||
dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
|
||||
usb_host ? "attached" : "detached");
|
||||
|
||||
/*
|
||||
* Set usb_id_short flag to avoid running charger detection logic
|
||||
* in case usb host.
|
||||
*/
|
||||
info->otg.id_short = usb_host;
|
||||
|
||||
/* Disable VBUS path before enabling the 5V boost */
|
||||
ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
|
||||
@ -685,135 +687,109 @@ static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, otg.id_nb);
|
||||
struct extcon_dev *edev = info->otg.cable;
|
||||
int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
|
||||
|
||||
dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
|
||||
usb_host ? "attached" : "detached");
|
||||
|
||||
/*
|
||||
* Set usb_id_short flag to avoid running charger detection logic
|
||||
* in case usb host.
|
||||
*/
|
||||
info->otg.id_short = usb_host;
|
||||
schedule_work(&info->otg.work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void charger_init_hw_regs(struct axp288_chrg_info *info)
|
||||
static int charger_init_hw_regs(struct axp288_chrg_info *info)
|
||||
{
|
||||
int ret, cc, cv;
|
||||
unsigned int val;
|
||||
|
||||
/* Program temperature thresholds */
|
||||
ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_V_LTF_CHRG, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_V_HTF_CHRG, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do not turn-off charger o/p after charge cycle ends */
|
||||
ret = regmap_update_bits(info->regmap,
|
||||
AXP20X_CHRG_CTRL2,
|
||||
CNTL2_CHG_OUT_TURNON, 1);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_CHRG_CTRL2, ret);
|
||||
|
||||
/* Enable interrupts */
|
||||
ret = regmap_update_bits(info->regmap,
|
||||
AXP20X_IRQ2_EN,
|
||||
BAT_IRQ_CFG_BAT_MASK, 1);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_IRQ2_EN, ret);
|
||||
|
||||
ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
|
||||
TEMP_IRQ_CFG_MASK, 1);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_IRQ3_EN, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup ending condition for charging to be 10% of I(chrg) */
|
||||
ret = regmap_update_bits(info->regmap,
|
||||
AXP20X_CHRG_CTRL1,
|
||||
CHRG_CCCV_ITERM_20P, 0);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_CHRG_CTRL1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable OCV-SOC curve calibration */
|
||||
ret = regmap_update_bits(info->regmap,
|
||||
AXP20X_CC_CTRL,
|
||||
FG_CNTL_OCV_ADJ_EN, 0);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
|
||||
AXP20X_CC_CTRL, ret);
|
||||
|
||||
/* Init charging current and voltage */
|
||||
info->max_cc = info->pdata->max_cc;
|
||||
info->max_cv = info->pdata->max_cv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read current charge voltage and current limit */
|
||||
ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
|
||||
if (ret < 0) {
|
||||
/* Assume default if cannot read */
|
||||
info->cc = info->pdata->def_cc;
|
||||
info->cv = info->pdata->def_cv;
|
||||
} else {
|
||||
/* Determine charge voltage */
|
||||
cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
|
||||
switch (cv) {
|
||||
case CHRG_CCCV_CV_4100MV:
|
||||
info->cv = CV_4100MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4150MV:
|
||||
info->cv = CV_4150MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4200MV:
|
||||
info->cv = CV_4200MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4350MV:
|
||||
info->cv = CV_4350MV;
|
||||
break;
|
||||
default:
|
||||
info->cv = INT_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine charge current limit */
|
||||
cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
|
||||
cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
|
||||
info->cc = cc;
|
||||
|
||||
/* Program default charging voltage and current */
|
||||
cc = min(info->pdata->def_cc, info->max_cc);
|
||||
cv = min(info->pdata->def_cv, info->max_cv);
|
||||
|
||||
ret = axp288_charger_set_cc(info, cc);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev,
|
||||
"error(%d) in setting CC\n", ret);
|
||||
|
||||
ret = axp288_charger_set_cv(info, cv);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev,
|
||||
"error(%d) in setting CV\n", ret);
|
||||
dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
|
||||
AXP20X_CHRG_CTRL1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Determine charge voltage */
|
||||
cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
|
||||
switch (cv) {
|
||||
case CHRG_CCCV_CV_4100MV:
|
||||
info->cv = CV_4100MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4150MV:
|
||||
info->cv = CV_4150MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4200MV:
|
||||
info->cv = CV_4200MV;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4350MV:
|
||||
info->cv = CV_4350MV;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine charge current limit */
|
||||
cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
|
||||
cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
|
||||
info->cc = cc;
|
||||
|
||||
/*
|
||||
* Do not allow the user to configure higher settings then those
|
||||
* set by the firmware
|
||||
*/
|
||||
info->max_cv = info->cv;
|
||||
info->max_cc = info->cc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp288_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, i, pirq;
|
||||
struct axp288_chrg_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config charger_cfg = {};
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
@ -821,15 +797,8 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||
info->pdev = pdev;
|
||||
info->regmap = axp20x->regmap;
|
||||
info->regmap_irqc = axp20x->regmap_irqc;
|
||||
info->pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!info->pdata) {
|
||||
/* Try ACPI provided pdata via device properties */
|
||||
if (!device_property_present(&pdev->dev,
|
||||
"axp288_charger_data\n"))
|
||||
dev_err(&pdev->dev, "failed to get platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
info->cable.chg_type = -1;
|
||||
info->is_charger_enabled = -1;
|
||||
|
||||
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
|
||||
if (info->cable.edev == NULL) {
|
||||
@ -838,63 +807,55 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/* Register for extcon notification */
|
||||
INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
|
||||
info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
|
||||
ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
|
||||
&info->cable.nb);
|
||||
if (ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"failed to register extcon notifier for SDP %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
|
||||
&info->cable.nb);
|
||||
if (ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"failed to register extcon notifier for CDP %d\n", ret);
|
||||
extcon_unregister_notifier(info->cable.edev,
|
||||
EXTCON_CHG_USB_SDP, &info->cable.nb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
|
||||
&info->cable.nb);
|
||||
if (ret) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"failed to register extcon notifier for DCP %d\n", ret);
|
||||
extcon_unregister_notifier(info->cable.edev,
|
||||
EXTCON_CHG_USB_SDP, &info->cable.nb);
|
||||
extcon_unregister_notifier(info->cable.edev,
|
||||
EXTCON_CHG_USB_CDP, &info->cable.nb);
|
||||
return ret;
|
||||
info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_DEV_NAME);
|
||||
if (info->otg.cable == NULL) {
|
||||
dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
mutex_init(&info->lock);
|
||||
|
||||
ret = charger_init_hw_regs(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Register with power supply class */
|
||||
charger_cfg.drv_data = info;
|
||||
info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
|
||||
&charger_cfg);
|
||||
info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
|
||||
&charger_cfg);
|
||||
if (IS_ERR(info->psy_usb)) {
|
||||
dev_err(&pdev->dev, "failed to register power supply charger\n");
|
||||
ret = PTR_ERR(info->psy_usb);
|
||||
goto psy_reg_failed;
|
||||
dev_err(dev, "failed to register power supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register for extcon notification */
|
||||
INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
|
||||
info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt;
|
||||
info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt;
|
||||
info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt;
|
||||
for (i = 0; i < ARRAY_SIZE(cable_ids); i++) {
|
||||
ret = devm_extcon_register_notifier(dev, info->cable.edev,
|
||||
cable_ids[i], &info->cable.nb[i]);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register extcon notifier for %u: %d\n",
|
||||
cable_ids[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
schedule_work(&info->cable.work);
|
||||
|
||||
/* Register for OTG notification */
|
||||
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
|
||||
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
|
||||
ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST,
|
||||
&info->otg.id_nb);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "failed to register otg notifier\n");
|
||||
|
||||
if (info->otg.cable)
|
||||
info->otg.id_short = extcon_get_cable_state_(
|
||||
info->otg.cable, EXTCON_USB_HOST);
|
||||
ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
|
||||
EXTCON_USB_HOST, &info->otg.id_nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
|
||||
return ret;
|
||||
}
|
||||
schedule_work(&info->otg.work);
|
||||
|
||||
/* Register charger interrupts */
|
||||
for (i = 0; i < CHRG_INTR_END; i++) {
|
||||
@ -903,8 +864,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||
if (info->irq[i] < 0) {
|
||||
dev_warn(&info->pdev->dev,
|
||||
"failed to get virtual interrupt=%d\n", pirq);
|
||||
ret = info->irq[i];
|
||||
goto intr_reg_failed;
|
||||
return info->irq[i];
|
||||
}
|
||||
ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
|
||||
NULL, axp288_charger_irq_thread_handler,
|
||||
@ -912,51 +872,22 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
|
||||
info->irq[i]);
|
||||
goto intr_reg_failed;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
charger_init_hw_regs(info);
|
||||
|
||||
return 0;
|
||||
|
||||
intr_reg_failed:
|
||||
if (info->otg.cable)
|
||||
extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
|
||||
&info->otg.id_nb);
|
||||
power_supply_unregister(info->psy_usb);
|
||||
psy_reg_failed:
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
|
||||
&info->cable.nb);
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
|
||||
&info->cable.nb);
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
|
||||
&info->cable.nb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int axp288_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct axp288_chrg_info *info = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (info->otg.cable)
|
||||
extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
|
||||
&info->otg.id_nb);
|
||||
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
|
||||
&info->cable.nb);
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
|
||||
&info->cable.nb);
|
||||
extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
|
||||
&info->cable.nb);
|
||||
power_supply_unregister(info->psy_usb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id axp288_charger_id_table[] = {
|
||||
{ .name = "axp288_charger" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
|
||||
|
||||
static struct platform_driver axp288_charger_driver = {
|
||||
.probe = axp288_charger_probe,
|
||||
.remove = axp288_charger_remove,
|
||||
.id_table = axp288_charger_id_table,
|
||||
.driver = {
|
||||
.name = "axp288_charger",
|
||||
},
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
||||
#define CHRG_STAT_BAT_VALID (1 << 4)
|
||||
@ -49,23 +50,6 @@
|
||||
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
|
||||
#define CHRG_CCCV_CHG_EN (1 << 7)
|
||||
|
||||
#define CV_4100 4100 /* 4100mV */
|
||||
#define CV_4150 4150 /* 4150mV */
|
||||
#define CV_4200 4200 /* 4200mV */
|
||||
#define CV_4350 4350 /* 4350mV */
|
||||
|
||||
#define TEMP_IRQ_CFG_QWBTU (1 << 0)
|
||||
#define TEMP_IRQ_CFG_WBTU (1 << 1)
|
||||
#define TEMP_IRQ_CFG_QWBTO (1 << 2)
|
||||
#define TEMP_IRQ_CFG_WBTO (1 << 3)
|
||||
#define TEMP_IRQ_CFG_MASK 0xf
|
||||
|
||||
#define FG_IRQ_CFG_LOWBATT_WL2 (1 << 0)
|
||||
#define FG_IRQ_CFG_LOWBATT_WL1 (1 << 1)
|
||||
#define FG_IRQ_CFG_LOWBATT_MASK 0x3
|
||||
#define LOWBAT_IRQ_STAT_LOWBATT_WL2 (1 << 0)
|
||||
#define LOWBAT_IRQ_STAT_LOWBATT_WL1 (1 << 1)
|
||||
|
||||
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
|
||||
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
|
||||
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
|
||||
@ -73,17 +57,15 @@
|
||||
#define FG_CNTL_CC_EN (1 << 6)
|
||||
#define FG_CNTL_GAUGE_EN (1 << 7)
|
||||
|
||||
#define FG_15BIT_WORD_VALID (1 << 15)
|
||||
#define FG_15BIT_VAL_MASK 0x7fff
|
||||
|
||||
#define FG_REP_CAP_VALID (1 << 7)
|
||||
#define FG_REP_CAP_VAL_MASK 0x7F
|
||||
|
||||
#define FG_DES_CAP1_VALID (1 << 7)
|
||||
#define FG_DES_CAP1_VAL_MASK 0x7F
|
||||
#define FG_DES_CAP0_VAL_MASK 0xFF
|
||||
#define FG_DES_CAP_RES_LSB 1456 /* 1.456mAhr */
|
||||
|
||||
#define FG_CC_MTR1_VALID (1 << 7)
|
||||
#define FG_CC_MTR1_VAL_MASK 0x7F
|
||||
#define FG_CC_MTR0_VAL_MASK 0xFF
|
||||
#define FG_DES_CC_RES_LSB 1456 /* 1.456mAhr */
|
||||
|
||||
#define FG_OCV_CAP_VALID (1 << 7)
|
||||
@ -104,9 +86,7 @@
|
||||
|
||||
/* 1.1mV per LSB expressed in uV */
|
||||
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
|
||||
/* properties converted to tenths of degrees, uV, uA, uW */
|
||||
#define PROP_TEMP(a) ((a) * 10)
|
||||
#define UNPROP_TEMP(a) ((a) / 10)
|
||||
/* properties converted to uV, uA */
|
||||
#define PROP_VOLT(a) ((a) * 1000)
|
||||
#define PROP_CURR(a) ((a) * 1000)
|
||||
|
||||
@ -122,13 +102,13 @@ enum {
|
||||
|
||||
struct axp288_fg_info {
|
||||
struct platform_device *pdev;
|
||||
struct axp20x_fg_pdata *pdata;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
int irq[AXP288_FG_INTR_NUM];
|
||||
struct power_supply *bat;
|
||||
struct mutex lock;
|
||||
int status;
|
||||
int max_volt;
|
||||
struct delayed_work status_monitor;
|
||||
struct dentry *debug_file;
|
||||
};
|
||||
@ -138,22 +118,14 @@ static enum power_supply_property fuel_gauge_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TEMP_MAX,
|
||||
POWER_SUPPLY_PROP_TEMP_MIN,
|
||||
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
|
||||
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
};
|
||||
|
||||
static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
|
||||
@ -169,8 +141,10 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
@ -187,6 +161,44 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
int ret;
|
||||
|
||||
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);
|
||||
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);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return ret & FG_15BIT_VAL_MASK;
|
||||
}
|
||||
|
||||
static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
int ret;
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */
|
||||
return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
|
||||
}
|
||||
|
||||
static int pmic_read_adc_val(const char *name, int *raw_val,
|
||||
struct axp288_fg_info *info)
|
||||
{
|
||||
@ -247,24 +259,15 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data)
|
||||
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_OCVH[%02x] : %02x\n",
|
||||
seq_printf(s, " FG_OCV[%02x] : %04x\n",
|
||||
AXP288_FG_OCVH_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));
|
||||
seq_printf(s, " FG_OCVL[%02x] : %02x\n",
|
||||
AXP288_FG_OCVL_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));
|
||||
seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",
|
||||
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_reg_readb(info, AXP288_FG_DES_CAP1_REG));
|
||||
seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",
|
||||
AXP288_FG_DES_CAP0_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG));
|
||||
seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n",
|
||||
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_reg_readb(info, AXP288_FG_CC_MTR1_REG));
|
||||
seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n",
|
||||
AXP288_FG_CC_MTR0_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_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));
|
||||
@ -417,143 +420,27 @@ current_read_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int temp_to_adc(struct axp288_fg_info *info, int tval)
|
||||
{
|
||||
int rntc = 0, i, ret, adc_val;
|
||||
int rmin, rmax, tmin, tmax;
|
||||
int tcsz = info->pdata->tcsz;
|
||||
|
||||
/* get the Rntc resitance value for this temp */
|
||||
if (tval > info->pdata->thermistor_curve[0][1]) {
|
||||
rntc = info->pdata->thermistor_curve[0][0];
|
||||
} else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) {
|
||||
rntc = info->pdata->thermistor_curve[tcsz-1][0];
|
||||
} else {
|
||||
for (i = 1; i < tcsz; i++) {
|
||||
if (tval > info->pdata->thermistor_curve[i][1]) {
|
||||
rmin = info->pdata->thermistor_curve[i-1][0];
|
||||
rmax = info->pdata->thermistor_curve[i][0];
|
||||
tmin = info->pdata->thermistor_curve[i-1][1];
|
||||
tmax = info->pdata->thermistor_curve[i][1];
|
||||
rntc = rmin + ((rmax - rmin) *
|
||||
(tval - tmin) / (tmax - tmin));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we need the current to calculate the proper adc voltage */
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
|
||||
ret = 0x30;
|
||||
}
|
||||
|
||||
/*
|
||||
* temperature is proportional to NTS thermistor resistance
|
||||
* ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
|
||||
* [12-bit ADC VAL] = R_NTC(Ω) * current / 800
|
||||
*/
|
||||
adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800;
|
||||
|
||||
return adc_val;
|
||||
}
|
||||
|
||||
static int adc_to_temp(struct axp288_fg_info *info, int adc_val)
|
||||
{
|
||||
int ret, r, i, tval = 0;
|
||||
int rmin, rmax, tmin, tmax;
|
||||
int tcsz = info->pdata->tcsz;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
|
||||
ret = 0x30;
|
||||
}
|
||||
|
||||
/*
|
||||
* temperature is proportional to NTS thermistor resistance
|
||||
* ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
|
||||
* R_NTC(Ω) = [12-bit ADC VAL] * 800 / current
|
||||
*/
|
||||
r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3)));
|
||||
|
||||
if (r < info->pdata->thermistor_curve[0][0]) {
|
||||
tval = info->pdata->thermistor_curve[0][1];
|
||||
} else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) {
|
||||
tval = info->pdata->thermistor_curve[tcsz-1][1];
|
||||
} else {
|
||||
for (i = 1; i < tcsz; i++) {
|
||||
if (r < info->pdata->thermistor_curve[i][0]) {
|
||||
rmin = info->pdata->thermistor_curve[i-1][0];
|
||||
rmax = info->pdata->thermistor_curve[i][0];
|
||||
tmin = info->pdata->thermistor_curve[i-1][1];
|
||||
tmax = info->pdata->thermistor_curve[i][1];
|
||||
tval = tmin + ((tmax - tmin) *
|
||||
(r - rmin) / (rmax - rmin));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tval;
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp)
|
||||
{
|
||||
int ret, raw_val = 0;
|
||||
|
||||
ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
|
||||
if (ret < 0)
|
||||
goto temp_read_fail;
|
||||
|
||||
*btemp = adc_to_temp(info, raw_val);
|
||||
|
||||
temp_read_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
|
||||
{
|
||||
int ret, value;
|
||||
int ret;
|
||||
|
||||
/* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);
|
||||
if (ret < 0)
|
||||
goto vocv_read_fail;
|
||||
value = ret << 4;
|
||||
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
|
||||
if (ret >= 0)
|
||||
*vocv = VOLTAGE_FROM_ADC(ret);
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);
|
||||
if (ret < 0)
|
||||
goto vocv_read_fail;
|
||||
value |= (ret & 0xf);
|
||||
|
||||
*vocv = VOLTAGE_FROM_ADC(value);
|
||||
vocv_read_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
|
||||
{
|
||||
int temp, vocv;
|
||||
int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
|
||||
ret = fuel_gauge_get_btemp(info, &temp);
|
||||
if (ret < 0)
|
||||
goto health_read_fail;
|
||||
int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
|
||||
ret = fuel_gauge_get_vocv(info, &vocv);
|
||||
if (ret < 0)
|
||||
goto health_read_fail;
|
||||
|
||||
if (vocv > info->pdata->max_volt)
|
||||
if (vocv > info->max_volt)
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
else if (temp > info->pdata->max_temp)
|
||||
health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else if (temp < info->pdata->min_temp)
|
||||
health = POWER_SUPPLY_HEALTH_COLD;
|
||||
else if (vocv < info->pdata->min_volt)
|
||||
health = POWER_SUPPLY_HEALTH_DEAD;
|
||||
else
|
||||
health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
@ -561,28 +448,6 @@ health_read_fail:
|
||||
return health;
|
||||
}
|
||||
|
||||
static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret, adc_val;
|
||||
|
||||
/* program temperature threshold as 1/16 ADC value */
|
||||
adc_val = temp_to_adc(info, info->pdata->max_temp);
|
||||
ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret, adc_val;
|
||||
|
||||
/* program temperature threshold as 1/16 ADC value */
|
||||
adc_val = temp_to_adc(info, info->pdata->min_temp);
|
||||
ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_property(struct power_supply *ps,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
@ -643,58 +508,25 @@ static int fuel_gauge_get_property(struct power_supply *ps,
|
||||
goto fuel_gauge_read_err;
|
||||
val->intval = (ret & 0x0f);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
ret = fuel_gauge_get_btemp(info, &value);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
val->intval = PROP_TEMP(value);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_MAX:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
val->intval = PROP_TEMP(info->pdata->max_temp);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
|
||||
val->intval = PROP_TEMP(info->pdata->min_temp);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG);
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
value = (ret & FG_CC_MTR1_VAL_MASK) << 8;
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
value |= (ret & FG_CC_MTR0_VAL_MASK);
|
||||
val->intval = value * FG_DES_CAP_RES_LSB;
|
||||
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
value = (ret & FG_DES_CAP1_VAL_MASK) << 8;
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
value |= (ret & FG_DES_CAP0_VAL_MASK);
|
||||
val->intval = value * FG_DES_CAP_RES_LSB;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
val->intval = PROP_CURR(info->pdata->design_cap);
|
||||
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = PROP_VOLT(info->pdata->max_volt);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
val->intval = PROP_VOLT(info->pdata->min_volt);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = info->pdata->battid;
|
||||
val->intval = PROP_VOLT(info->max_volt);
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&info->lock);
|
||||
@ -718,35 +550,6 @@ static int fuel_gauge_set_property(struct power_supply *ps,
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
info->status = val->intval;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
|
||||
if ((val->intval < PD_DEF_MIN_TEMP) ||
|
||||
(val->intval > PD_DEF_MAX_TEMP)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
info->pdata->min_temp = UNPROP_TEMP(val->intval);
|
||||
ret = fuel_gauge_set_low_btemp_alert(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev,
|
||||
"temp alert min set fail:%d\n", ret);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_MAX:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
if ((val->intval < PD_DEF_MIN_TEMP) ||
|
||||
(val->intval > PD_DEF_MAX_TEMP)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
info->pdata->max_temp = UNPROP_TEMP(val->intval);
|
||||
ret = fuel_gauge_set_high_btemp_alert(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev,
|
||||
"temp alert max set fail:%d\n", ret);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||
if ((val->intval < 0) || (val->intval > 15)) {
|
||||
ret = -EINVAL;
|
||||
@ -774,11 +577,6 @@ static int fuel_gauge_property_is_writeable(struct power_supply *psy,
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
case POWER_SUPPLY_PROP_TEMP_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_MAX:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||
ret = 1;
|
||||
break;
|
||||
@ -863,158 +661,6 @@ static const struct power_supply_desc fuel_gauge_desc = {
|
||||
.external_power_changed = fuel_gauge_external_power_changed,
|
||||
};
|
||||
|
||||
static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_val;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = (ret & FG_REP_CAP_VAL_MASK);
|
||||
|
||||
if (ret > FG_LOW_CAP_WARN_THR)
|
||||
reg_val = FG_LOW_CAP_WARN_THR;
|
||||
else if (ret > FG_LOW_CAP_CRIT_THR)
|
||||
reg_val = FG_LOW_CAP_CRIT_THR;
|
||||
else
|
||||
reg_val = FG_LOW_CAP_SHDN_THR;
|
||||
|
||||
reg_val |= FG_LOW_CAP_THR1_VAL;
|
||||
ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
|
||||
if (ret < 0)
|
||||
goto fg_prog_ocv_fail;
|
||||
else
|
||||
val = (ret & ~CHRG_CCCV_CV_MASK);
|
||||
|
||||
switch (info->pdata->max_volt) {
|
||||
case CV_4100:
|
||||
val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);
|
||||
break;
|
||||
case CV_4150:
|
||||
val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);
|
||||
break;
|
||||
case CV_4200:
|
||||
val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
|
||||
break;
|
||||
case CV_4350:
|
||||
val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);
|
||||
break;
|
||||
default:
|
||||
val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val);
|
||||
fg_prog_ocv_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_program_design_cap(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info,
|
||||
AXP288_FG_DES_CAP1_REG, info->pdata->cap1);
|
||||
if (ret < 0)
|
||||
goto fg_prog_descap_fail;
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info,
|
||||
AXP288_FG_DES_CAP0_REG, info->pdata->cap0);
|
||||
|
||||
fg_prog_descap_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret = 0, i;
|
||||
|
||||
for (i = 0; i < OCV_CURVE_SIZE; i++) {
|
||||
ret = fuel_gauge_reg_writeb(info,
|
||||
AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]);
|
||||
if (ret < 0)
|
||||
goto fg_prog_ocv_fail;
|
||||
}
|
||||
|
||||
fg_prog_ocv_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info,
|
||||
AXP288_FG_RDC1_REG, info->pdata->rdc1);
|
||||
if (ret < 0)
|
||||
goto fg_prog_ocv_fail;
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info,
|
||||
AXP288_FG_RDC0_REG, info->pdata->rdc0);
|
||||
|
||||
fg_prog_ocv_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fuel_gauge_init_config_regs(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* check if the config data is already
|
||||
* programmed and if so just return.
|
||||
*/
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0) {
|
||||
dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");
|
||||
} else if (!(ret & FG_DES_CAP1_VALID)) {
|
||||
dev_info(&info->pdev->dev, "FG data needs to be initialized\n");
|
||||
} else {
|
||||
dev_info(&info->pdev->dev, "FG data is already initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_program_vbatt_full(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_program_design_cap(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_program_rdc_vals(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_program_ocv_curve(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_set_lowbatt_thresholds(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);
|
||||
}
|
||||
|
||||
static void fuel_gauge_init_irq(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret, i, pirq;
|
||||
@ -1052,29 +698,6 @@ intr_failed:
|
||||
}
|
||||
}
|
||||
|
||||
static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = fuel_gauge_set_high_btemp_alert(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret);
|
||||
|
||||
ret = fuel_gauge_set_low_btemp_alert(info);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret);
|
||||
|
||||
/* enable interrupts */
|
||||
val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN);
|
||||
val |= TEMP_IRQ_CFG_MASK;
|
||||
fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val);
|
||||
|
||||
val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN);
|
||||
val |= FG_IRQ_CFG_LOWBATT_MASK;
|
||||
val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val);
|
||||
}
|
||||
|
||||
static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -1090,15 +713,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||
info->regmap = axp20x->regmap;
|
||||
info->regmap_irqc = axp20x->regmap_irqc;
|
||||
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
info->pdata = pdev->dev.platform_data;
|
||||
if (!info->pdata)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
mutex_init(&info->lock);
|
||||
INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(ret & FG_DES_CAP1_VALID)) {
|
||||
dev_err(&pdev->dev, "axp288 not configured by firmware\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
|
||||
case CHRG_CCCV_CV_4100MV:
|
||||
info->max_volt = 4100;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4150MV:
|
||||
info->max_volt = 4150;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4200MV:
|
||||
info->max_volt = 4200;
|
||||
break;
|
||||
case CHRG_CCCV_CV_4350MV:
|
||||
info->max_volt = 4350;
|
||||
break;
|
||||
}
|
||||
|
||||
psy_cfg.drv_data = info;
|
||||
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
|
||||
if (IS_ERR(info->bat)) {
|
||||
@ -1108,12 +755,10 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
fuel_gauge_create_debugfs(info);
|
||||
fuel_gauge_init_config_regs(info);
|
||||
fuel_gauge_init_irq(info);
|
||||
fuel_gauge_init_hw_regs(info);
|
||||
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id axp288_fg_id_table[] = {
|
||||
|
@ -1569,6 +1569,11 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||
acpi_id =
|
||||
acpi_match_device(client->dev.driver->acpi_match_table,
|
||||
&client->dev);
|
||||
if (!acpi_id) {
|
||||
dev_err(&client->dev, "failed to match device name\n");
|
||||
ret = -ENODEV;
|
||||
goto error_1;
|
||||
}
|
||||
name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
|
||||
}
|
||||
if (!name) {
|
||||
|
@ -144,10 +144,7 @@
|
||||
* so the first read after a fault returns the latched value and subsequent
|
||||
* reads return the current value. In order to return the fault status
|
||||
* to the user, have the interrupt handler save the reg's value and retrieve
|
||||
* it in the appropriate health/status routine. Each routine has its own
|
||||
* flag indicating whether it should use the value stored by the last run
|
||||
* of the interrupt handler or do an actual reg read. That way each routine
|
||||
* can report back whatever fault may have occured.
|
||||
* it in the appropriate health/status routine.
|
||||
*/
|
||||
struct bq24190_dev_info {
|
||||
struct i2c_client *client;
|
||||
@ -159,10 +156,6 @@ struct bq24190_dev_info {
|
||||
unsigned int gpio_int;
|
||||
unsigned int irq;
|
||||
struct mutex f_reg_lock;
|
||||
bool first_time;
|
||||
bool charger_health_valid;
|
||||
bool battery_health_valid;
|
||||
bool battery_status_valid;
|
||||
u8 f_reg;
|
||||
u8 ss_reg;
|
||||
u8 watchdog;
|
||||
@ -199,7 +192,7 @@ static const int bq24190_cvc_vreg_values[] = {
|
||||
4400000
|
||||
};
|
||||
|
||||
/* REG06[1:0] (TREG) in tenths of degrees Celcius */
|
||||
/* REG06[1:0] (TREG) in tenths of degrees Celsius */
|
||||
static const int bq24190_ictrc_treg_values[] = {
|
||||
600, 800, 1000, 1200
|
||||
};
|
||||
@ -636,21 +629,11 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
u8 v;
|
||||
int health, ret;
|
||||
int health;
|
||||
|
||||
mutex_lock(&bdi->f_reg_lock);
|
||||
|
||||
if (bdi->charger_health_valid) {
|
||||
v = bdi->f_reg;
|
||||
bdi->charger_health_valid = false;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
} else {
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_F, &v);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
v = bdi->f_reg;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
|
||||
/*
|
||||
@ -937,18 +920,8 @@ static int bq24190_battery_get_status(struct bq24190_dev_info *bdi,
|
||||
int status, ret;
|
||||
|
||||
mutex_lock(&bdi->f_reg_lock);
|
||||
|
||||
if (bdi->battery_status_valid) {
|
||||
chrg_fault = bdi->f_reg;
|
||||
bdi->battery_status_valid = false;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
} else {
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
chrg_fault = bdi->f_reg;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
|
||||
chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
|
||||
@ -996,21 +969,11 @@ static int bq24190_battery_get_health(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
u8 v;
|
||||
int health, ret;
|
||||
int health;
|
||||
|
||||
mutex_lock(&bdi->f_reg_lock);
|
||||
|
||||
if (bdi->battery_health_valid) {
|
||||
v = bdi->f_reg;
|
||||
bdi->battery_health_valid = false;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
} else {
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_F, &v);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
v = bdi->f_reg;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
@ -1197,9 +1160,12 @@ static const struct power_supply_desc bq24190_battery_desc = {
|
||||
static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||
{
|
||||
struct bq24190_dev_info *bdi = data;
|
||||
bool alert_userspace = false;
|
||||
const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK;
|
||||
const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK
|
||||
| BQ24190_REG_F_NTC_FAULT_MASK;
|
||||
bool alert_charger = false, alert_battery = false;
|
||||
u8 ss_reg = 0, f_reg = 0;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
pm_runtime_get_sync(bdi->dev);
|
||||
|
||||
@ -1209,6 +1175,32 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
} while (f_reg && ++i < 2);
|
||||
|
||||
if (f_reg != bdi->f_reg) {
|
||||
dev_info(bdi->dev,
|
||||
"Fault: boost %d, charge %d, battery %d, ntc %d\n",
|
||||
!!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK),
|
||||
!!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK),
|
||||
!!(f_reg & BQ24190_REG_F_BAT_FAULT_MASK),
|
||||
!!(f_reg & BQ24190_REG_F_NTC_FAULT_MASK));
|
||||
|
||||
mutex_lock(&bdi->f_reg_lock);
|
||||
if ((bdi->f_reg & battery_mask_f) != (f_reg & battery_mask_f))
|
||||
alert_battery = true;
|
||||
if ((bdi->f_reg & ~battery_mask_f) != (f_reg & ~battery_mask_f))
|
||||
alert_charger = true;
|
||||
bdi->f_reg = f_reg;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
}
|
||||
|
||||
if (ss_reg != bdi->ss_reg) {
|
||||
/*
|
||||
* The device is in host mode so when PG_STAT goes from 1->0
|
||||
@ -1225,47 +1217,17 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||
ret);
|
||||
}
|
||||
|
||||
if ((bdi->ss_reg & battery_mask_ss) != (ss_reg & battery_mask_ss))
|
||||
alert_battery = true;
|
||||
if ((bdi->ss_reg & ~battery_mask_ss) != (ss_reg & ~battery_mask_ss))
|
||||
alert_charger = true;
|
||||
bdi->ss_reg = ss_reg;
|
||||
alert_userspace = true;
|
||||
}
|
||||
|
||||
mutex_lock(&bdi->f_reg_lock);
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (f_reg != bdi->f_reg) {
|
||||
bdi->f_reg = f_reg;
|
||||
bdi->charger_health_valid = true;
|
||||
bdi->battery_health_valid = true;
|
||||
bdi->battery_status_valid = true;
|
||||
|
||||
alert_userspace = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
/*
|
||||
* Sometimes bq24190 gives a steady trickle of interrupts even
|
||||
* though the watchdog timer is turned off and neither the STATUS
|
||||
* nor FAULT registers have changed. Weed out these sprurious
|
||||
* interrupts so userspace isn't alerted for no reason.
|
||||
* In addition, the chip always generates an interrupt after
|
||||
* register reset so we should ignore that one (the very first
|
||||
* interrupt received).
|
||||
*/
|
||||
if (alert_userspace) {
|
||||
if (!bdi->first_time) {
|
||||
power_supply_changed(bdi->charger);
|
||||
power_supply_changed(bdi->battery);
|
||||
} else {
|
||||
bdi->first_time = false;
|
||||
}
|
||||
}
|
||||
if (alert_charger)
|
||||
power_supply_changed(bdi->charger);
|
||||
if (alert_battery)
|
||||
power_supply_changed(bdi->battery);
|
||||
|
||||
out:
|
||||
pm_runtime_put_sync(bdi->dev);
|
||||
@ -1300,6 +1262,10 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)
|
||||
goto out;
|
||||
|
||||
ret = bq24190_set_mode_host(bdi);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
|
||||
out:
|
||||
pm_runtime_put_sync(bdi->dev);
|
||||
return ret;
|
||||
@ -1375,10 +1341,8 @@ static int bq24190_probe(struct i2c_client *client,
|
||||
bdi->model = id->driver_data;
|
||||
strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
|
||||
mutex_init(&bdi->f_reg_lock);
|
||||
bdi->first_time = true;
|
||||
bdi->charger_health_valid = false;
|
||||
bdi->battery_health_valid = false;
|
||||
bdi->battery_status_valid = false;
|
||||
bdi->f_reg = 0;
|
||||
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
|
||||
|
||||
i2c_set_clientdata(client, bdi);
|
||||
|
||||
@ -1392,22 +1356,13 @@ static int bq24190_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
|
||||
bq24190_irq_handler_thread,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"bq24190-charger", bdi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Can't set up irq handler\n");
|
||||
goto out1;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
ret = bq24190_hw_init(bdi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Hardware init failed\n");
|
||||
goto out2;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
charger_cfg.drv_data = bdi;
|
||||
@ -1418,7 +1373,7 @@ static int bq24190_probe(struct i2c_client *client,
|
||||
if (IS_ERR(bdi->charger)) {
|
||||
dev_err(dev, "Can't register charger\n");
|
||||
ret = PTR_ERR(bdi->charger);
|
||||
goto out2;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
battery_cfg.drv_data = bdi;
|
||||
@ -1427,27 +1382,39 @@ static int bq24190_probe(struct i2c_client *client,
|
||||
if (IS_ERR(bdi->battery)) {
|
||||
dev_err(dev, "Can't register battery\n");
|
||||
ret = PTR_ERR(bdi->battery);
|
||||
goto out3;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = bq24190_sysfs_create_group(bdi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't create sysfs entries\n");
|
||||
goto out3;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
|
||||
bq24190_irq_handler_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"bq24190-charger", bdi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Can't set up irq handler\n");
|
||||
goto out4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out4:
|
||||
power_supply_unregister(bdi->battery);
|
||||
bq24190_sysfs_remove_group(bdi);
|
||||
|
||||
out3:
|
||||
power_supply_unregister(bdi->charger);
|
||||
power_supply_unregister(bdi->battery);
|
||||
|
||||
out2:
|
||||
pm_runtime_disable(dev);
|
||||
power_supply_unregister(bdi->charger);
|
||||
|
||||
out1:
|
||||
pm_runtime_disable(dev);
|
||||
if (bdi->gpio_int)
|
||||
gpio_free(bdi->gpio_int);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1488,12 +1455,13 @@ static int bq24190_pm_resume(struct device *dev)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
||||
|
||||
bdi->charger_health_valid = false;
|
||||
bdi->battery_health_valid = false;
|
||||
bdi->battery_status_valid = false;
|
||||
bdi->f_reg = 0;
|
||||
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
|
||||
|
||||
pm_runtime_get_sync(bdi->dev);
|
||||
bq24190_register_reset(bdi);
|
||||
bq24190_set_mode_host(bdi);
|
||||
bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
|
||||
pm_runtime_put_sync(bdi->dev);
|
||||
|
||||
/* Things may have changed while suspended so alert upper layer */
|
||||
|
@ -50,6 +50,8 @@ struct bq24735 {
|
||||
struct bq24735_platform *pdata;
|
||||
struct mutex lock;
|
||||
struct gpio_desc *status_gpio;
|
||||
struct delayed_work poll;
|
||||
u32 poll_interval;
|
||||
bool charging;
|
||||
};
|
||||
|
||||
@ -105,26 +107,6 @@ static int bq24735_update_word(struct i2c_client *client, u8 reg,
|
||||
return bq24735_write_word(client, reg, tmp);
|
||||
}
|
||||
|
||||
static inline int bq24735_enable_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int bq24735_config_charger(struct bq24735 *charger)
|
||||
{
|
||||
struct bq24735_platform *pdata = charger->pdata;
|
||||
@ -176,6 +158,31 @@ static int bq24735_config_charger(struct bq24735 *charger)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bq24735_enable_charging(struct bq24735 *charger)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (charger->pdata->ext_control)
|
||||
return 0;
|
||||
|
||||
ret = bq24735_config_charger(charger);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static bool bq24735_charger_is_present(struct bq24735 *charger)
|
||||
{
|
||||
if (charger->status_gpio) {
|
||||
@ -185,7 +192,7 @@ static bool bq24735_charger_is_present(struct bq24735 *charger)
|
||||
|
||||
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
|
||||
if (ac < 0) {
|
||||
dev_err(&charger->client->dev,
|
||||
dev_dbg(&charger->client->dev,
|
||||
"Failed to read charger options : %d\n",
|
||||
ac);
|
||||
return false;
|
||||
@ -210,11 +217,8 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)
|
||||
return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
|
||||
}
|
||||
|
||||
static irqreturn_t bq24735_charger_isr(int irq, void *devid)
|
||||
static void bq24735_update(struct bq24735 *charger)
|
||||
{
|
||||
struct power_supply *psy = devid;
|
||||
struct bq24735 *charger = to_bq24735(psy);
|
||||
|
||||
mutex_lock(&charger->lock);
|
||||
|
||||
if (charger->charging && bq24735_charger_is_present(charger))
|
||||
@ -224,11 +228,29 @@ static irqreturn_t bq24735_charger_isr(int irq, void *devid)
|
||||
|
||||
mutex_unlock(&charger->lock);
|
||||
|
||||
power_supply_changed(psy);
|
||||
power_supply_changed(charger->charger);
|
||||
}
|
||||
|
||||
static irqreturn_t bq24735_charger_isr(int irq, void *devid)
|
||||
{
|
||||
struct power_supply *psy = devid;
|
||||
struct bq24735 *charger = to_bq24735(psy);
|
||||
|
||||
bq24735_update(charger);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void bq24735_poll(struct work_struct *work)
|
||||
{
|
||||
struct bq24735 *charger = container_of(work, struct bq24735, poll.work);
|
||||
|
||||
bq24735_update(charger);
|
||||
|
||||
schedule_delayed_work(&charger->poll,
|
||||
msecs_to_jiffies(charger->poll_interval));
|
||||
}
|
||||
|
||||
static int bq24735_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
@ -276,7 +298,6 @@ static int bq24735_charger_set_property(struct power_supply *psy,
|
||||
mutex_unlock(&charger->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
bq24735_config_charger(charger);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_DISCHARGING:
|
||||
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||||
@ -395,7 +416,7 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!charger->status_gpio || bq24735_charger_is_present(charger)) {
|
||||
if (bq24735_charger_is_present(charger)) {
|
||||
ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
|
||||
@ -416,16 +437,7 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||
"device id mismatch. 0x000b != 0x%04x\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bq24735_config_charger(charger);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed in configuring charger");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check for AC adapter presence */
|
||||
if (bq24735_charger_is_present(charger)) {
|
||||
ret = bq24735_enable_charging(charger);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to enable charging\n");
|
||||
@ -456,11 +468,32 @@ static int bq24735_charger_probe(struct i2c_client *client,
|
||||
client->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = device_property_read_u32(&client->dev, "poll-interval",
|
||||
&charger->poll_interval);
|
||||
if (ret)
|
||||
return 0;
|
||||
if (!charger->poll_interval)
|
||||
return 0;
|
||||
|
||||
INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
|
||||
schedule_delayed_work(&charger->poll,
|
||||
msecs_to_jiffies(charger->poll_interval));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq24735_charger_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bq24735 *charger = i2c_get_clientdata(client);
|
||||
|
||||
if (charger->poll_interval)
|
||||
cancel_delayed_work_sync(&charger->poll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bq24735_charger_id[] = {
|
||||
{ "bq24735-charger", 0 },
|
||||
{}
|
||||
@ -479,6 +512,7 @@ static struct i2c_driver bq24735_charger_driver = {
|
||||
.of_match_table = bq24735_match_ids,
|
||||
},
|
||||
.probe = bq24735_charger_probe,
|
||||
.remove = bq24735_charger_remove,
|
||||
.id_table = bq24735_charger_id,
|
||||
};
|
||||
|
||||
|
@ -22,8 +22,14 @@
|
||||
* http://www.ti.com/product/bq27010
|
||||
* http://www.ti.com/product/bq27210
|
||||
* http://www.ti.com/product/bq27500
|
||||
* http://www.ti.com/product/bq27510-g1
|
||||
* http://www.ti.com/product/bq27510-g2
|
||||
* http://www.ti.com/product/bq27510-g3
|
||||
* http://www.ti.com/product/bq27520-g4
|
||||
* http://www.ti.com/product/bq27520-g1
|
||||
* http://www.ti.com/product/bq27520-g2
|
||||
* http://www.ti.com/product/bq27520-g3
|
||||
* http://www.ti.com/product/bq27520-g4
|
||||
* http://www.ti.com/product/bq27530-g1
|
||||
* http://www.ti.com/product/bq27531-g1
|
||||
* http://www.ti.com/product/bq27541-g1
|
||||
@ -145,7 +151,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_DCAP] = 0x76,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
},
|
||||
[BQ27500] = {
|
||||
[BQ2750X] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||
@ -164,7 +170,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
},
|
||||
[BQ27510] = {
|
||||
[BQ2751X] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||
@ -183,6 +189,158 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_DCAP] = 0x2e,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
},
|
||||
[BQ27500] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = 0x18,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27510G1] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = 0x18,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27510G2] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = 0x18,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27510G3] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTES] = 0x1a,
|
||||
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x1e,
|
||||
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_SOC] = 0x20,
|
||||
[BQ27XXX_REG_DCAP] = 0x2e,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
},
|
||||
[BQ27520G1] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = 0x18,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27520G2] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x36,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = 0x18,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27520G3] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x36,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = 0x26,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x2a,
|
||||
[BQ27XXX_REG_AE] = 0x22,
|
||||
[BQ27XXX_REG_SOC] = 0x2c,
|
||||
[BQ27XXX_REG_DCAP] = 0x3c,
|
||||
[BQ27XXX_REG_AP] = 0x24,
|
||||
},
|
||||
[BQ27520G4] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
[BQ27XXX_REG_INT_TEMP] = 0x28,
|
||||
[BQ27XXX_REG_VOLT] = 0x08,
|
||||
[BQ27XXX_REG_AI] = 0x14,
|
||||
[BQ27XXX_REG_FLAGS] = 0x0a,
|
||||
[BQ27XXX_REG_TTE] = 0x16,
|
||||
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTES] = 0x1c,
|
||||
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_NAC] = 0x0c,
|
||||
[BQ27XXX_REG_FCC] = 0x12,
|
||||
[BQ27XXX_REG_CYCT] = 0x1e,
|
||||
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_SOC] = 0x20,
|
||||
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
},
|
||||
[BQ27530] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
@ -303,7 +461,7 @@ static enum power_supply_property bq27010_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27500_battery_props[] = {
|
||||
static enum power_supply_property bq2750x_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
@ -321,7 +479,7 @@ static enum power_supply_property bq27500_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27510_battery_props[] = {
|
||||
static enum power_supply_property bq2751x_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
@ -339,6 +497,165 @@ static enum power_supply_property bq27510_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27500_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27510g1_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27510g2_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27510g3_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27520g1_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27520g2_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27520g3_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27520g4_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27530_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
@ -421,8 +738,16 @@ static struct {
|
||||
} bq27xxx_battery_props[] = {
|
||||
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
|
||||
BQ27XXX_PROP(BQ27010, bq27010_battery_props),
|
||||
BQ27XXX_PROP(BQ2750X, bq2750x_battery_props),
|
||||
BQ27XXX_PROP(BQ2751X, bq2751x_battery_props),
|
||||
BQ27XXX_PROP(BQ27500, bq27500_battery_props),
|
||||
BQ27XXX_PROP(BQ27510, bq27510_battery_props),
|
||||
BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props),
|
||||
BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props),
|
||||
BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props),
|
||||
BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props),
|
||||
BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props),
|
||||
BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props),
|
||||
BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props),
|
||||
BQ27XXX_PROP(BQ27530, bq27530_battery_props),
|
||||
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
|
||||
BQ27XXX_PROP(BQ27545, bq27545_battery_props),
|
||||
@ -674,13 +999,26 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
|
||||
*/
|
||||
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
|
||||
{
|
||||
if (di->chip == BQ27500 || di->chip == BQ27510 ||
|
||||
di->chip == BQ27541 || di->chip == BQ27545)
|
||||
switch (di->chip) {
|
||||
case BQ2750X:
|
||||
case BQ2751X:
|
||||
case BQ27500:
|
||||
case BQ27510G1:
|
||||
case BQ27510G2:
|
||||
case BQ27510G3:
|
||||
case BQ27520G1:
|
||||
case BQ27520G2:
|
||||
case BQ27520G3:
|
||||
case BQ27520G4:
|
||||
case BQ27541:
|
||||
case BQ27545:
|
||||
return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
|
||||
if (di->chip == BQ27530 || di->chip == BQ27421)
|
||||
case BQ27530:
|
||||
case BQ27421:
|
||||
return flags & BQ27XXX_FLAG_OT;
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -148,9 +148,17 @@ static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
|
||||
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
||||
{ "bq27200", BQ27000 },
|
||||
{ "bq27210", BQ27010 },
|
||||
{ "bq27500", BQ27500 },
|
||||
{ "bq27510", BQ27510 },
|
||||
{ "bq27520", BQ27510 },
|
||||
{ "bq27500", BQ2750X },
|
||||
{ "bq27510", BQ2751X },
|
||||
{ "bq27520", BQ2751X },
|
||||
{ "bq27500-1", BQ27500 },
|
||||
{ "bq27510g1", BQ27510G1 },
|
||||
{ "bq27510g2", BQ27510G2 },
|
||||
{ "bq27510g3", BQ27510G3 },
|
||||
{ "bq27520g1", BQ27520G1 },
|
||||
{ "bq27520g2", BQ27520G2 },
|
||||
{ "bq27520g3", BQ27520G3 },
|
||||
{ "bq27520g4", BQ27520G4 },
|
||||
{ "bq27530", BQ27530 },
|
||||
{ "bq27531", BQ27530 },
|
||||
{ "bq27541", BQ27541 },
|
||||
@ -173,6 +181,14 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
|
||||
{ .compatible = "ti,bq27500" },
|
||||
{ .compatible = "ti,bq27510" },
|
||||
{ .compatible = "ti,bq27520" },
|
||||
{ .compatible = "ti,bq27500-1" },
|
||||
{ .compatible = "ti,bq27510g1" },
|
||||
{ .compatible = "ti,bq27510g2" },
|
||||
{ .compatible = "ti,bq27510g3" },
|
||||
{ .compatible = "ti,bq27520g1" },
|
||||
{ .compatible = "ti,bq27520g2" },
|
||||
{ .compatible = "ti,bq27520g3" },
|
||||
{ .compatible = "ti,bq27520g4" },
|
||||
{ .compatible = "ti,bq27530" },
|
||||
{ .compatible = "ti,bq27531" },
|
||||
{ .compatible = "ti,bq27541" },
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio.h> /* For legacy platform data */
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -23,7 +23,7 @@
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/power/gpio-charger.h>
|
||||
|
||||
@ -34,6 +34,8 @@ struct gpio_charger {
|
||||
|
||||
struct power_supply *charger;
|
||||
struct power_supply_desc charger_desc;
|
||||
struct gpio_desc *gpiod;
|
||||
bool legacy_gpio_requested;
|
||||
};
|
||||
|
||||
static irqreturn_t gpio_charger_irq(int irq, void *devid)
|
||||
@ -58,7 +60,8 @@ static int gpio_charger_get_property(struct power_supply *psy,
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = !!gpio_get_value_cansleep(pdata->gpio);
|
||||
val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
|
||||
/* This xor is only ever used with legacy pdata GPIO */
|
||||
val->intval ^= pdata->gpio_active_low;
|
||||
break;
|
||||
default:
|
||||
@ -78,7 +81,6 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
|
||||
struct device_node *np = dev->of_node;
|
||||
struct gpio_charger_platform_data *pdata;
|
||||
const char *chargetype;
|
||||
enum of_gpio_flags flags;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
@ -89,16 +91,6 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->name = np->name;
|
||||
|
||||
pdata->gpio = of_get_gpio_flags(np, 0, &flags);
|
||||
if (pdata->gpio < 0) {
|
||||
if (pdata->gpio != -EPROBE_DEFER)
|
||||
dev_err(dev, "could not get charger gpio\n");
|
||||
return ERR_PTR(pdata->gpio);
|
||||
}
|
||||
|
||||
pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
|
||||
|
||||
pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
ret = of_property_read_string(np, "charger-type", &chargetype);
|
||||
if (ret >= 0) {
|
||||
@ -144,11 +136,6 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpio_is_valid(pdata->gpio)) {
|
||||
dev_err(&pdev->dev, "Invalid gpio pin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
|
||||
GFP_KERNEL);
|
||||
if (!gpio_charger) {
|
||||
@ -156,6 +143,45 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will fetch a GPIO descriptor from device tree, ACPI or
|
||||
* boardfile descriptor tables. It's good to try this first.
|
||||
*/
|
||||
gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
|
||||
|
||||
/*
|
||||
* If this fails and we're not using device tree, try the
|
||||
* legacy platform data method.
|
||||
*/
|
||||
if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) {
|
||||
/* Non-DT: use legacy GPIO numbers */
|
||||
if (!gpio_is_valid(pdata->gpio)) {
|
||||
dev_err(&pdev->dev, "Invalid gpio pin in pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request gpio pin: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
gpio_charger->legacy_gpio_requested = true;
|
||||
ret = gpio_direction_input(pdata->gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to set gpio to input: %d\n",
|
||||
ret);
|
||||
goto err_gpio_free;
|
||||
}
|
||||
/* Then convert this to gpiod for now */
|
||||
gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
|
||||
} else if (IS_ERR(gpio_charger->gpiod)) {
|
||||
/* Just try again if this happens */
|
||||
if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&pdev->dev, "error getting GPIO descriptor\n");
|
||||
return PTR_ERR(gpio_charger->gpiod);
|
||||
}
|
||||
|
||||
charger_desc = &gpio_charger->charger_desc;
|
||||
|
||||
charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
|
||||
@ -169,17 +195,6 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||
psy_cfg.of_node = pdev->dev.of_node;
|
||||
psy_cfg.drv_data = gpio_charger;
|
||||
|
||||
ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret);
|
||||
goto err_free;
|
||||
}
|
||||
ret = gpio_direction_input(pdata->gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret);
|
||||
goto err_gpio_free;
|
||||
}
|
||||
|
||||
gpio_charger->pdata = pdata;
|
||||
|
||||
gpio_charger->charger = power_supply_register(&pdev->dev,
|
||||
@ -191,7 +206,7 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||
goto err_gpio_free;
|
||||
}
|
||||
|
||||
irq = gpio_to_irq(pdata->gpio);
|
||||
irq = gpiod_to_irq(gpio_charger->gpiod);
|
||||
if (irq > 0) {
|
||||
ret = request_any_context_irq(irq, gpio_charger_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
@ -209,8 +224,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_gpio_free:
|
||||
gpio_free(pdata->gpio);
|
||||
err_free:
|
||||
if (gpio_charger->legacy_gpio_requested)
|
||||
gpio_free(pdata->gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -223,7 +238,8 @@ static int gpio_charger_remove(struct platform_device *pdev)
|
||||
|
||||
power_supply_unregister(gpio_charger->charger);
|
||||
|
||||
gpio_free(gpio_charger->pdata->gpio);
|
||||
if (gpio_charger->legacy_gpio_requested)
|
||||
gpio_free(gpio_charger->pdata->gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,795 +0,0 @@
|
||||
/*
|
||||
* intel_mid_battery.c - Intel MID PMIC Battery Driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define DRIVER_NAME "pmic_battery"
|
||||
|
||||
/*********************************************************************
|
||||
* Generic defines
|
||||
*********************************************************************/
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0444);
|
||||
MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
|
||||
|
||||
#define PMIC_BATT_DRV_INFO_UPDATED 1
|
||||
#define PMIC_BATT_PRESENT 1
|
||||
#define PMIC_BATT_NOT_PRESENT 0
|
||||
#define PMIC_USB_PRESENT PMIC_BATT_PRESENT
|
||||
#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT
|
||||
|
||||
/* pmic battery register related */
|
||||
#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2
|
||||
#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1)
|
||||
#define PMIC_BATT_CHR_STEMP_MASK (1 << 2)
|
||||
#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3)
|
||||
#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4)
|
||||
#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5)
|
||||
#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6)
|
||||
#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7)
|
||||
#define PMIC_BATT_CHR_EXCPT_MASK 0x86
|
||||
|
||||
#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
|
||||
#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
|
||||
|
||||
/* pmic ipc related */
|
||||
#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
|
||||
#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
|
||||
|
||||
/* types of battery charging */
|
||||
enum batt_charge_type {
|
||||
BATT_USBOTG_500MA_CHARGE,
|
||||
BATT_USBOTG_TRICKLE_CHARGE,
|
||||
};
|
||||
|
||||
/* valid battery events */
|
||||
enum batt_event {
|
||||
BATT_EVENT_BATOVP_EXCPT,
|
||||
BATT_EVENT_USBOVP_EXCPT,
|
||||
BATT_EVENT_TEMP_EXCPT,
|
||||
BATT_EVENT_DCLMT_EXCPT,
|
||||
BATT_EVENT_EXCPT
|
||||
};
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* Battery properties
|
||||
*********************************************************************/
|
||||
|
||||
/*
|
||||
* pmic battery info
|
||||
*/
|
||||
struct pmic_power_module_info {
|
||||
bool is_dev_info_updated;
|
||||
struct device *dev;
|
||||
/* pmic battery data */
|
||||
unsigned long update_time; /* jiffies when data read */
|
||||
unsigned int usb_is_present;
|
||||
unsigned int batt_is_present;
|
||||
unsigned int batt_health;
|
||||
unsigned int usb_health;
|
||||
unsigned int batt_status;
|
||||
unsigned int batt_charge_now; /* in mAS */
|
||||
unsigned int batt_prev_charge_full; /* in mAS */
|
||||
unsigned int batt_charge_rate; /* in units per second */
|
||||
|
||||
struct power_supply *usb;
|
||||
struct power_supply *batt;
|
||||
int irq; /* GPE_ID or IRQ# */
|
||||
struct workqueue_struct *monitor_wqueue;
|
||||
struct delayed_work monitor_battery;
|
||||
struct work_struct handler;
|
||||
};
|
||||
|
||||
static unsigned int delay_time = 2000; /* in ms */
|
||||
|
||||
/*
|
||||
* pmic ac properties
|
||||
*/
|
||||
static enum power_supply_property pmic_usb_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
/*
|
||||
* pmic battery properties
|
||||
*/
|
||||
static enum power_supply_property pmic_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Glue functions for talking to the IPC
|
||||
*/
|
||||
|
||||
struct battery_property {
|
||||
u32 capacity; /* Charger capacity */
|
||||
u8 crnt; /* Quick charge current value*/
|
||||
u8 volt; /* Fine adjustment of constant charge voltage */
|
||||
u8 prot; /* CHRGPROT register value */
|
||||
u8 prot2; /* CHRGPROT1 register value */
|
||||
u8 timer; /* Charging timer */
|
||||
};
|
||||
|
||||
#define IPCMSG_BATTERY 0xEF
|
||||
|
||||
/* Battery coulomb counter accumulator commands */
|
||||
#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
|
||||
#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
|
||||
#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
|
||||
|
||||
/**
|
||||
* pmic_scu_ipc_battery_cc_read - read battery cc
|
||||
* @value: battery coulomb counter read
|
||||
*
|
||||
* Reads the battery couloumb counter value, returns 0 on success, or
|
||||
* an error code
|
||||
*
|
||||
* This function may sleep. Locking for SCU accesses is handled for
|
||||
* the caller.
|
||||
*/
|
||||
static int pmic_scu_ipc_battery_cc_read(u32 *value)
|
||||
{
|
||||
return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
|
||||
NULL, 0, value, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_scu_ipc_battery_property_get - fetch properties
|
||||
* @prop: battery properties
|
||||
*
|
||||
* Retrieve the battery properties from the power management
|
||||
*
|
||||
* This function may sleep. Locking for SCU accesses is handled for
|
||||
* the caller.
|
||||
*/
|
||||
static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
|
||||
{
|
||||
u32 data[3];
|
||||
u8 *p = (u8 *)&data[1];
|
||||
int err = intel_scu_ipc_command(IPCMSG_BATTERY,
|
||||
IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3);
|
||||
|
||||
prop->capacity = data[0];
|
||||
prop->crnt = *p++;
|
||||
prop->volt = *p++;
|
||||
prop->prot = *p++;
|
||||
prop->prot2 = *p++;
|
||||
prop->timer = *p++;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_scu_ipc_set_charger - set charger
|
||||
* @charger: charger to select
|
||||
*
|
||||
* Switch the charging mode for the SCU
|
||||
*/
|
||||
|
||||
static int pmic_scu_ipc_set_charger(int charger)
|
||||
{
|
||||
return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger);
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_log_event - log battery events
|
||||
* @event: battery event to be logged
|
||||
* Context: can sleep
|
||||
*
|
||||
* There are multiple battery events which may be of interest to users;
|
||||
* this battery function logs the different battery events onto the
|
||||
* kernel log messages.
|
||||
*/
|
||||
static void pmic_battery_log_event(enum batt_event event)
|
||||
{
|
||||
printk(KERN_WARNING "pmic-battery: ");
|
||||
switch (event) {
|
||||
case BATT_EVENT_BATOVP_EXCPT:
|
||||
printk(KERN_CONT "battery overvoltage condition\n");
|
||||
break;
|
||||
case BATT_EVENT_USBOVP_EXCPT:
|
||||
printk(KERN_CONT "usb charger overvoltage condition\n");
|
||||
break;
|
||||
case BATT_EVENT_TEMP_EXCPT:
|
||||
printk(KERN_CONT "high battery temperature condition\n");
|
||||
break;
|
||||
case BATT_EVENT_DCLMT_EXCPT:
|
||||
printk(KERN_CONT "over battery charge current condition\n");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_CONT "charger/battery exception %d\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_read_status - read battery status information
|
||||
* @pbi: device info structure to update the read information
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC power source information need to be updated based on the data read
|
||||
* from the PMIC battery registers.
|
||||
*
|
||||
*/
|
||||
static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
|
||||
{
|
||||
unsigned int update_time_intrvl;
|
||||
unsigned int chrg_val;
|
||||
u32 ccval;
|
||||
u8 r8;
|
||||
struct battery_property batt_prop;
|
||||
int batt_present = 0;
|
||||
int usb_present = 0;
|
||||
int batt_exception = 0;
|
||||
|
||||
/* make sure the last batt_status read happened delay_time before */
|
||||
if (pbi->update_time && time_before(jiffies, pbi->update_time +
|
||||
msecs_to_jiffies(delay_time)))
|
||||
return;
|
||||
|
||||
update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
|
||||
pbi->update_time = jiffies;
|
||||
|
||||
/* read coulomb counter registers and schrgint register */
|
||||
if (pmic_scu_ipc_battery_cc_read(&ccval)) {
|
||||
dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
|
||||
dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* set pmic_power_module_info members based on pmic register values
|
||||
* read.
|
||||
*/
|
||||
|
||||
/* set batt_is_present */
|
||||
if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
|
||||
pbi->batt_is_present = PMIC_BATT_PRESENT;
|
||||
batt_present = 1;
|
||||
} else {
|
||||
pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
/* set batt_health */
|
||||
if (batt_present) {
|
||||
if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
|
||||
batt_exception = 1;
|
||||
} else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
|
||||
batt_exception = 1;
|
||||
} else {
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
|
||||
/* PMIC will change charging current automatically */
|
||||
pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set usb_is_present */
|
||||
if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
|
||||
pbi->usb_is_present = PMIC_USB_PRESENT;
|
||||
usb_present = 1;
|
||||
} else {
|
||||
pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
}
|
||||
|
||||
if (usb_present) {
|
||||
if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
|
||||
} else {
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
}
|
||||
|
||||
chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
|
||||
|
||||
/* set batt_prev_charge_full to battery capacity the first time */
|
||||
if (!pbi->is_dev_info_updated) {
|
||||
if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
|
||||
dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
pbi->batt_prev_charge_full = batt_prop.capacity;
|
||||
}
|
||||
|
||||
/* set batt_status */
|
||||
if (batt_present && !batt_exception) {
|
||||
if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
|
||||
pbi->batt_prev_charge_full = chrg_val;
|
||||
} else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
} else {
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
}
|
||||
}
|
||||
|
||||
/* set batt_charge_rate */
|
||||
if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
|
||||
if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
|
||||
if (pbi->batt_charge_now - chrg_val) {
|
||||
pbi->batt_charge_rate = ((pbi->batt_charge_now -
|
||||
chrg_val) * 1000 * 60) /
|
||||
update_time_intrvl;
|
||||
}
|
||||
} else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
|
||||
if (chrg_val - pbi->batt_charge_now) {
|
||||
pbi->batt_charge_rate = ((chrg_val -
|
||||
pbi->batt_charge_now) * 1000 * 60) /
|
||||
update_time_intrvl;
|
||||
}
|
||||
} else
|
||||
pbi->batt_charge_rate = 0;
|
||||
} else {
|
||||
pbi->batt_charge_rate = -1;
|
||||
}
|
||||
|
||||
/* batt_charge_now */
|
||||
if (batt_present && !batt_exception)
|
||||
pbi->batt_charge_now = chrg_val;
|
||||
else
|
||||
pbi->batt_charge_now = -1;
|
||||
|
||||
pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_usb_get_property - usb power source get property
|
||||
* @psy: usb power supply context
|
||||
* @psp: usb power source property
|
||||
* @val: usb power source property value
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC usb power source property needs to be provided to power_supply
|
||||
* subsytem for it to provide the information to users.
|
||||
*/
|
||||
static int pmic_usb_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
|
||||
|
||||
/* update pmic_power_module_info members */
|
||||
pmic_battery_read_status(pbi);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = pbi->usb_is_present;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = pbi->usb_health;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned long mAStouAh(unsigned long v)
|
||||
{
|
||||
/* seconds to hours, mA to µA */
|
||||
return (v * 1000) / 3600;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_get_property - battery power source get property
|
||||
* @psy: battery power supply context
|
||||
* @psp: battery power source property
|
||||
* @val: battery power source property value
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery power source property needs to be provided to power_supply
|
||||
* subsytem for it to provide the information to users.
|
||||
*/
|
||||
static int pmic_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
|
||||
|
||||
/* update pmic_power_module_info members */
|
||||
pmic_battery_read_status(pbi);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = pbi->batt_status;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = pbi->batt_health;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = pbi->batt_is_present;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
val->intval = mAStouAh(pbi->batt_charge_now);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
val->intval = mAStouAh(pbi->batt_prev_charge_full);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_monitor - monitor battery status
|
||||
* @work: work structure
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery status needs to be monitored for any change
|
||||
* and information needs to be frequently updated.
|
||||
*/
|
||||
static void pmic_battery_monitor(struct work_struct *work)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = container_of(work,
|
||||
struct pmic_power_module_info, monitor_battery.work);
|
||||
|
||||
/* update pmic_power_module_info members */
|
||||
pmic_battery_read_status(pbi);
|
||||
queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_set_charger - set battery charger
|
||||
* @pbi: device info structure
|
||||
* @chrg: charge mode to set battery charger in
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery charger needs to be enabled based on the usb charge
|
||||
* capabilities connected to the platform.
|
||||
*/
|
||||
static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
|
||||
enum batt_charge_type chrg)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* set usblmt bits and chrgcntl register bits appropriately */
|
||||
switch (chrg) {
|
||||
case BATT_USBOTG_500MA_CHARGE:
|
||||
retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
|
||||
break;
|
||||
case BATT_USBOTG_TRICKLE_CHARGE:
|
||||
retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
|
||||
break;
|
||||
default:
|
||||
dev_warn(pbi->dev, "%s(): out of range usb charger "
|
||||
"charge detected\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
|
||||
__func__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_interrupt_handler - pmic battery interrupt handler
|
||||
* Context: interrupt context
|
||||
*
|
||||
* PMIC battery interrupt handler which will be called with either
|
||||
* battery full condition occurs or usb otg & battery connect
|
||||
* condition occurs.
|
||||
*/
|
||||
static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = dev;
|
||||
|
||||
schedule_work(&pbi->handler);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_handle_intrpt - pmic battery service interrupt
|
||||
* @work: work structure
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery needs to either update the battery status as full
|
||||
* if it detects battery full condition caused the interrupt or needs
|
||||
* to enable battery charger if it detects usb and battery detect
|
||||
* caused the source of interrupt.
|
||||
*/
|
||||
static void pmic_battery_handle_intrpt(struct work_struct *work)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = container_of(work,
|
||||
struct pmic_power_module_info, handler);
|
||||
enum batt_charge_type chrg;
|
||||
u8 r8;
|
||||
|
||||
if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
|
||||
dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
/* find the cause of the interrupt */
|
||||
if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
|
||||
pbi->batt_is_present = PMIC_BATT_PRESENT;
|
||||
} else {
|
||||
pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
pmic_battery_log_event(BATT_EVENT_EXCPT);
|
||||
return;
|
||||
} else {
|
||||
pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
|
||||
if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
|
||||
u32 ccval;
|
||||
pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
if (pmic_scu_ipc_battery_cc_read(&ccval)) {
|
||||
dev_warn(pbi->dev, "%s(): ipc config cmd "
|
||||
"failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
pbi->batt_prev_charge_full = ccval &
|
||||
PMIC_BATT_ADC_ACCCHRGVAL_MASK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
|
||||
pbi->usb_is_present = PMIC_USB_PRESENT;
|
||||
} else {
|
||||
pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
|
||||
pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup battery charging */
|
||||
|
||||
#if 0
|
||||
/* check usb otg power capability and set charger accordingly */
|
||||
retval = langwell_udc_maxpower(&power);
|
||||
if (retval) {
|
||||
dev_warn(pbi->dev,
|
||||
"%s(): usb otg power query failed with error code %d\n",
|
||||
__func__, retval);
|
||||
return;
|
||||
}
|
||||
|
||||
if (power >= 500)
|
||||
chrg = BATT_USBOTG_500MA_CHARGE;
|
||||
else
|
||||
#endif
|
||||
chrg = BATT_USBOTG_TRICKLE_CHARGE;
|
||||
|
||||
/* enable battery charging */
|
||||
if (pmic_battery_set_charger(pbi, chrg)) {
|
||||
dev_warn(pbi->dev,
|
||||
"%s(): failed to set up battery charging\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(pbi->dev,
|
||||
"pmic-battery: %s() - setting up battery charger successful\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Description of power supplies
|
||||
*/
|
||||
static const struct power_supply_desc pmic_usb_desc = {
|
||||
.name = "pmic-usb",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = pmic_usb_props,
|
||||
.num_properties = ARRAY_SIZE(pmic_usb_props),
|
||||
.get_property = pmic_usb_get_property,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc pmic_batt_desc = {
|
||||
.name = "pmic-batt",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = pmic_battery_props,
|
||||
.num_properties = ARRAY_SIZE(pmic_battery_props),
|
||||
.get_property = pmic_battery_get_property,
|
||||
};
|
||||
|
||||
/**
|
||||
* pmic_battery_probe - pmic battery initialize
|
||||
* @irq: pmic battery device irq
|
||||
* @dev: pmic battery device structure
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery initializes its internal data structue and other
|
||||
* infrastructure components for it to work as expected.
|
||||
*/
|
||||
static int probe(int irq, struct device *dev)
|
||||
{
|
||||
int retval = 0;
|
||||
struct pmic_power_module_info *pbi;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
|
||||
dev_dbg(dev, "pmic-battery: found pmic battery device\n");
|
||||
|
||||
pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
|
||||
if (!pbi) {
|
||||
dev_err(dev, "%s(): memory allocation failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pbi->dev = dev;
|
||||
pbi->irq = irq;
|
||||
dev_set_drvdata(dev, pbi);
|
||||
psy_cfg.drv_data = pbi;
|
||||
|
||||
/* initialize all required framework before enabling interrupts */
|
||||
INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
|
||||
INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
|
||||
pbi->monitor_wqueue = alloc_workqueue(dev_name(dev), WQ_MEM_RECLAIM, 0);
|
||||
if (!pbi->monitor_wqueue) {
|
||||
dev_err(dev, "%s(): wqueue init failed\n", __func__);
|
||||
retval = -ESRCH;
|
||||
goto wqueue_failed;
|
||||
}
|
||||
|
||||
/* register interrupt */
|
||||
retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
|
||||
0, DRIVER_NAME, pbi);
|
||||
if (retval) {
|
||||
dev_err(dev, "%s(): cannot get IRQ\n", __func__);
|
||||
goto requestirq_failed;
|
||||
}
|
||||
|
||||
/* register pmic-batt with power supply subsystem */
|
||||
pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg);
|
||||
if (IS_ERR(pbi->batt)) {
|
||||
dev_err(dev,
|
||||
"%s(): failed to register pmic battery device with power supply subsystem\n",
|
||||
__func__);
|
||||
retval = PTR_ERR(pbi->batt);
|
||||
goto power_reg_failed;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
|
||||
"registration with power supply subsystem successful\n",
|
||||
__func__);
|
||||
|
||||
queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
|
||||
|
||||
/* register pmic-usb with power supply subsystem */
|
||||
pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg);
|
||||
if (IS_ERR(pbi->usb)) {
|
||||
dev_err(dev,
|
||||
"%s(): failed to register pmic usb device with power supply subsystem\n",
|
||||
__func__);
|
||||
retval = PTR_ERR(pbi->usb);
|
||||
goto power_reg_failed_1;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
|
||||
"registration with power supply subsystem successful\n",
|
||||
__func__);
|
||||
|
||||
return retval;
|
||||
|
||||
power_reg_failed_1:
|
||||
power_supply_unregister(pbi->batt);
|
||||
power_reg_failed:
|
||||
cancel_delayed_work_sync(&pbi->monitor_battery);
|
||||
requestirq_failed:
|
||||
destroy_workqueue(pbi->monitor_wqueue);
|
||||
wqueue_failed:
|
||||
kfree(pbi);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int platform_pmic_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
return probe(pdev->id, &pdev->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pmic_battery_remove - pmic battery finalize
|
||||
* @dev: pmic battery device structure
|
||||
* Context: can sleep
|
||||
*
|
||||
* PMIC battery finalizes its internal data structue and other
|
||||
* infrastructure components that it initialized in
|
||||
* pmic_battery_probe.
|
||||
*/
|
||||
|
||||
static int platform_pmic_battery_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pmic_power_module_info *pbi = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(pbi->irq, pbi);
|
||||
cancel_delayed_work_sync(&pbi->monitor_battery);
|
||||
destroy_workqueue(pbi->monitor_wqueue);
|
||||
|
||||
power_supply_unregister(pbi->usb);
|
||||
power_supply_unregister(pbi->batt);
|
||||
|
||||
cancel_work_sync(&pbi->handler);
|
||||
kfree(pbi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver platform_pmic_battery_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = platform_pmic_battery_probe,
|
||||
.remove = platform_pmic_battery_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(platform_pmic_battery_driver);
|
||||
|
||||
MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
|
||||
MODULE_LICENSE("GPL");
|
327
drivers/power/supply/max14656_charger_detector.c
Normal file
327
drivers/power/supply/max14656_charger_detector.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Maxim MAX14656 / AL32 USB Charger Detector driver
|
||||
*
|
||||
* Copyright (C) 2014 LG Electronics, Inc
|
||||
* Copyright (C) 2016 Alexander Kurz <akurz@blala.de>
|
||||
*
|
||||
* Components from Maxim AL32 Charger detection Driver for MX50 Yoshi Board
|
||||
* Copyright (C) Amazon Technologies Inc. All rights reserved.
|
||||
* Manish Lachwani (lachwani@lab126.com)
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#define MAX14656_MANUFACTURER "Maxim Integrated"
|
||||
#define MAX14656_NAME "max14656"
|
||||
|
||||
#define MAX14656_DEVICE_ID 0x00
|
||||
#define MAX14656_INTERRUPT_1 0x01
|
||||
#define MAX14656_INTERRUPT_2 0x02
|
||||
#define MAX14656_STATUS_1 0x03
|
||||
#define MAX14656_STATUS_2 0x04
|
||||
#define MAX14656_INTMASK_1 0x05
|
||||
#define MAX14656_INTMASK_2 0x06
|
||||
#define MAX14656_CONTROL_1 0x07
|
||||
#define MAX14656_CONTROL_2 0x08
|
||||
#define MAX14656_CONTROL_3 0x09
|
||||
|
||||
#define DEVICE_VENDOR_MASK 0xf0
|
||||
#define DEVICE_REV_MASK 0x0f
|
||||
#define INT_EN_REG_MASK BIT(4)
|
||||
#define CHG_TYPE_INT_MASK BIT(0)
|
||||
#define STATUS1_VB_VALID_MASK BIT(4)
|
||||
#define STATUS1_CHG_TYPE_MASK 0xf
|
||||
#define INT1_DCD_TIMEOUT_MASK BIT(7)
|
||||
#define CONTROL1_DEFAULT 0x0d
|
||||
#define CONTROL1_INT_EN BIT(4)
|
||||
#define CONTROL1_INT_ACTIVE_HIGH BIT(5)
|
||||
#define CONTROL1_EDGE BIT(7)
|
||||
#define CONTROL2_DEFAULT 0x8e
|
||||
#define CONTROL2_ADC_EN BIT(0)
|
||||
#define CONTROL3_DEFAULT 0x8d
|
||||
|
||||
enum max14656_chg_type {
|
||||
MAX14656_NO_CHARGER = 0,
|
||||
MAX14656_SDP_CHARGER,
|
||||
MAX14656_CDP_CHARGER,
|
||||
MAX14656_DCP_CHARGER,
|
||||
MAX14656_APPLE_500MA_CHARGER,
|
||||
MAX14656_APPLE_1A_CHARGER,
|
||||
MAX14656_APPLE_2A_CHARGER,
|
||||
MAX14656_SPECIAL_500MA_CHARGER,
|
||||
MAX14656_APPLE_12W,
|
||||
MAX14656_CHARGER_LAST
|
||||
};
|
||||
|
||||
static const struct max14656_chg_type_props {
|
||||
enum power_supply_type type;
|
||||
} chg_type_props[] = {
|
||||
{ POWER_SUPPLY_TYPE_UNKNOWN },
|
||||
{ POWER_SUPPLY_TYPE_USB },
|
||||
{ POWER_SUPPLY_TYPE_USB_CDP },
|
||||
{ POWER_SUPPLY_TYPE_USB_DCP },
|
||||
{ POWER_SUPPLY_TYPE_USB_DCP },
|
||||
{ POWER_SUPPLY_TYPE_USB_DCP },
|
||||
{ POWER_SUPPLY_TYPE_USB_DCP },
|
||||
{ POWER_SUPPLY_TYPE_USB_DCP },
|
||||
{ POWER_SUPPLY_TYPE_USB },
|
||||
};
|
||||
|
||||
struct max14656_chip {
|
||||
struct i2c_client *client;
|
||||
struct power_supply *detect_psy;
|
||||
struct power_supply_desc psy_desc;
|
||||
struct delayed_work irq_work;
|
||||
|
||||
int irq;
|
||||
int online;
|
||||
};
|
||||
|
||||
static int max14656_read_reg(struct i2c_client *client, int reg, u8 *val)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"i2c read fail: can't read from %02x: %d\n",
|
||||
reg, ret);
|
||||
return ret;
|
||||
}
|
||||
*val = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max14656_write_reg(struct i2c_client *client, int reg, u8 val)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"i2c write fail: can't write %02x to %02x: %d\n",
|
||||
val, reg, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max14656_read_block_reg(struct i2c_client *client, u8 reg,
|
||||
u8 length, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, reg, length, val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to block read reg 0x%x: %d\n",
|
||||
reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define REG_TOTAL_NUM 5
|
||||
static void max14656_irq_worker(struct work_struct *work)
|
||||
{
|
||||
struct max14656_chip *chip =
|
||||
container_of(work, struct max14656_chip, irq_work.work);
|
||||
|
||||
u8 buf[REG_TOTAL_NUM];
|
||||
u8 chg_type;
|
||||
int ret = 0;
|
||||
|
||||
ret = max14656_read_block_reg(chip->client, MAX14656_DEVICE_ID,
|
||||
REG_TOTAL_NUM, buf);
|
||||
|
||||
if ((buf[MAX14656_STATUS_1] & STATUS1_VB_VALID_MASK) &&
|
||||
(buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK)) {
|
||||
chg_type = buf[MAX14656_STATUS_1] & STATUS1_CHG_TYPE_MASK;
|
||||
if (chg_type < MAX14656_CHARGER_LAST)
|
||||
chip->psy_desc.type = chg_type_props[chg_type].type;
|
||||
else
|
||||
chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
chip->online = 1;
|
||||
} else {
|
||||
chip->online = 0;
|
||||
chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
power_supply_changed(chip->detect_psy);
|
||||
}
|
||||
|
||||
static irqreturn_t max14656_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct max14656_chip *chip = dev_id;
|
||||
|
||||
schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(100));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max14656_hw_init(struct max14656_chip *chip)
|
||||
{
|
||||
uint8_t val = 0;
|
||||
uint8_t rev;
|
||||
struct i2c_client *client = chip->client;
|
||||
|
||||
if (max14656_read_reg(client, MAX14656_DEVICE_ID, &val))
|
||||
return -ENODEV;
|
||||
|
||||
if ((val & DEVICE_VENDOR_MASK) != 0x20) {
|
||||
dev_err(&client->dev, "wrong vendor ID %d\n",
|
||||
((val & DEVICE_VENDOR_MASK) >> 4));
|
||||
return -ENODEV;
|
||||
}
|
||||
rev = val & DEVICE_REV_MASK;
|
||||
|
||||
/* Turn on ADC_EN */
|
||||
if (max14656_write_reg(client, MAX14656_CONTROL_2, CONTROL2_ADC_EN))
|
||||
return -EINVAL;
|
||||
|
||||
/* turn on interrupts and low power mode */
|
||||
if (max14656_write_reg(client, MAX14656_CONTROL_1,
|
||||
CONTROL1_DEFAULT |
|
||||
CONTROL1_INT_EN |
|
||||
CONTROL1_INT_ACTIVE_HIGH |
|
||||
CONTROL1_EDGE))
|
||||
return -EINVAL;
|
||||
|
||||
if (max14656_write_reg(client, MAX14656_INTMASK_1, 0x3))
|
||||
return -EINVAL;
|
||||
|
||||
if (max14656_write_reg(client, MAX14656_INTMASK_2, 0x1))
|
||||
return -EINVAL;
|
||||
|
||||
dev_info(&client->dev, "detected revision %d\n", rev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max14656_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max14656_chip *chip = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = chip->online;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = MAX14656_NAME;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = MAX14656_MANUFACTURER;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property max14656_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static int max14656_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct device *dev = &client->dev;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max14656_chip *chip;
|
||||
int irq = client->irq;
|
||||
int ret = 0;
|
||||
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "invalid irq number: %d\n", irq);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
psy_cfg.drv_data = chip;
|
||||
chip->client = client;
|
||||
chip->online = 0;
|
||||
chip->psy_desc.name = MAX14656_NAME;
|
||||
chip->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
chip->psy_desc.properties = max14656_battery_props;
|
||||
chip->psy_desc.num_properties = ARRAY_SIZE(max14656_battery_props);
|
||||
chip->psy_desc.get_property = max14656_get_property;
|
||||
chip->irq = irq;
|
||||
|
||||
ret = max14656_hw_init(chip);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
|
||||
INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker);
|
||||
|
||||
ret = devm_request_irq(dev, chip->irq, max14656_irq,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
MAX14656_NAME, chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "request_irq %d failed\n", chip->irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
enable_irq_wake(chip->irq);
|
||||
|
||||
chip->detect_psy = devm_power_supply_register(dev,
|
||||
&chip->psy_desc, &psy_cfg);
|
||||
if (IS_ERR(chip->detect_psy)) {
|
||||
dev_err(dev, "power_supply_register failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(2000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max14656_id[] = {
|
||||
{ "max14656", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max14656_id);
|
||||
|
||||
static const struct of_device_id max14656_match_table[] = {
|
||||
{ .compatible = "maxim,max14656", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max14656_match_table);
|
||||
|
||||
static struct i2c_driver max14656_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max14656",
|
||||
.of_match_table = max14656_match_table,
|
||||
},
|
||||
.probe = max14656_probe,
|
||||
.id_table = max14656_id,
|
||||
};
|
||||
module_i2c_driver(max14656_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MAX14656 USB charger detector");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -148,10 +148,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||
|
||||
charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
|
||||
GFP_KERNEL);
|
||||
if (charger == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot allocate memory.\n");
|
||||
if (!charger)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, charger);
|
||||
|
||||
@ -161,7 +159,7 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||
|
||||
psy_cfg.drv_data = charger;
|
||||
|
||||
charger->battery = power_supply_register(&pdev->dev,
|
||||
charger->battery = devm_power_supply_register(&pdev->dev,
|
||||
&max8997_battery_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(charger->battery)) {
|
||||
@ -172,14 +170,6 @@ static int max8997_battery_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8997_battery_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct charger_data *charger = platform_get_drvdata(pdev);
|
||||
|
||||
power_supply_unregister(charger->battery);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id max8997_battery_id[] = {
|
||||
{ "max8997-battery", 0 },
|
||||
{ }
|
||||
@ -191,7 +181,6 @@ static struct platform_driver max8997_battery_driver = {
|
||||
.name = "max8997-battery",
|
||||
},
|
||||
.probe = max8997_battery_probe,
|
||||
.remove = max8997_battery_remove,
|
||||
.id_table = max8997_battery_id,
|
||||
};
|
||||
|
||||
|
@ -393,7 +393,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct pcf50633_mbc *mbc;
|
||||
int ret;
|
||||
int i;
|
||||
u8 mbcs1;
|
||||
|
||||
@ -419,8 +418,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
|
||||
&psy_cfg);
|
||||
if (IS_ERR(mbc->adapter)) {
|
||||
dev_err(mbc->pcf->dev, "failed to register adapter\n");
|
||||
ret = PTR_ERR(mbc->adapter);
|
||||
return ret;
|
||||
return PTR_ERR(mbc->adapter);
|
||||
}
|
||||
|
||||
mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
|
||||
@ -428,8 +426,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mbc->usb)) {
|
||||
dev_err(mbc->pcf->dev, "failed to register usb\n");
|
||||
power_supply_unregister(mbc->adapter);
|
||||
ret = PTR_ERR(mbc->usb);
|
||||
return ret;
|
||||
return PTR_ERR(mbc->usb);
|
||||
}
|
||||
|
||||
mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
|
||||
@ -438,12 +435,10 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
|
||||
dev_err(mbc->pcf->dev, "failed to register ac\n");
|
||||
power_supply_unregister(mbc->adapter);
|
||||
power_supply_unregister(mbc->usb);
|
||||
ret = PTR_ERR(mbc->ac);
|
||||
return ret;
|
||||
return PTR_ERR(mbc->ac);
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
|
||||
if (ret)
|
||||
if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))
|
||||
dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
|
||||
|
||||
mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#define SMBB_CHG_VMAX 0x040
|
||||
#define SMBB_CHG_VSAFE 0x041
|
||||
@ -72,6 +73,8 @@
|
||||
#define BTC_CTRL_HOT_EXT_N BIT(0)
|
||||
|
||||
#define SMBB_USB_IMAX 0x344
|
||||
#define SMBB_USB_OTG_CTL 0x348
|
||||
#define OTG_CTL_EN BIT(0)
|
||||
#define SMBB_USB_ENUM_TIMER_STOP 0x34e
|
||||
#define ENUM_TIMER_STOP BIT(0)
|
||||
#define SMBB_USB_SEC_ACCESS 0x3d0
|
||||
@ -125,6 +128,9 @@ struct smbb_charger {
|
||||
struct power_supply *dc_psy;
|
||||
struct power_supply *bat_psy;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct regulator_desc otg_rdesc;
|
||||
struct regulator_dev *otg_reg;
|
||||
};
|
||||
|
||||
static const unsigned int smbb_usb_extcon_cable[] = {
|
||||
@ -378,7 +384,7 @@ static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
|
||||
struct smbb_charger *chg = _data;
|
||||
|
||||
smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
|
||||
extcon_set_cable_state_(chg->edev, EXTCON_USB,
|
||||
extcon_set_state_sync(chg->edev, EXTCON_USB,
|
||||
chg->status & STATUS_USBIN_VALID);
|
||||
power_supply_changed(chg->usb_psy);
|
||||
|
||||
@ -787,12 +793,56 @@ static const struct power_supply_desc dc_psy_desc = {
|
||||
.property_is_writeable = smbb_charger_writable_property,
|
||||
};
|
||||
|
||||
static int smbb_chg_otg_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smbb_charger *chg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
|
||||
rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,
|
||||
OTG_CTL_EN, OTG_CTL_EN);
|
||||
if (rc)
|
||||
dev_err(chg->dev, "failed to update OTG_CTL\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smbb_chg_otg_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smbb_charger *chg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
|
||||
rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,
|
||||
OTG_CTL_EN, 0);
|
||||
if (rc)
|
||||
dev_err(chg->dev, "failed to update OTG_CTL\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smbb_chg_otg_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smbb_charger *chg = rdev_get_drvdata(rdev);
|
||||
unsigned int value = 0;
|
||||
int rc;
|
||||
|
||||
rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value);
|
||||
if (rc)
|
||||
dev_err(chg->dev, "failed to read OTG_CTL\n");
|
||||
|
||||
return !!(value & OTG_CTL_EN);
|
||||
}
|
||||
|
||||
static const struct regulator_ops smbb_chg_otg_ops = {
|
||||
.enable = smbb_chg_otg_enable,
|
||||
.disable = smbb_chg_otg_disable,
|
||||
.is_enabled = smbb_chg_otg_is_enabled,
|
||||
};
|
||||
|
||||
static int smbb_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct power_supply_config bat_cfg = {};
|
||||
struct power_supply_config usb_cfg = {};
|
||||
struct power_supply_config dc_cfg = {};
|
||||
struct smbb_charger *chg;
|
||||
struct regulator_config config = { };
|
||||
int rc, i;
|
||||
|
||||
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
|
||||
@ -905,6 +955,26 @@ static int smbb_charger_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* otg regulator is used to control VBUS voltage direction
|
||||
* when USB switches between host and gadget mode
|
||||
*/
|
||||
chg->otg_rdesc.id = -1;
|
||||
chg->otg_rdesc.name = "otg-vbus";
|
||||
chg->otg_rdesc.ops = &smbb_chg_otg_ops;
|
||||
chg->otg_rdesc.owner = THIS_MODULE;
|
||||
chg->otg_rdesc.type = REGULATOR_VOLTAGE;
|
||||
chg->otg_rdesc.supply_name = "usb-otg-in";
|
||||
chg->otg_rdesc.of_match = "otg-vbus";
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.driver_data = chg;
|
||||
|
||||
chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc,
|
||||
&config);
|
||||
if (IS_ERR(chg->otg_reg))
|
||||
return PTR_ERR(chg->otg_reg);
|
||||
|
||||
chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,jeita-extended-temp-range");
|
||||
|
||||
|
274
drivers/power/supply/sbs-charger.c
Normal file
274
drivers/power/supply/sbs-charger.c
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Prodys S.L.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This adds support for sbs-charger compilant chips as defined here:
|
||||
* http://sbs-forum.org/specs/sbc110.pdf
|
||||
*
|
||||
* Implemetation based on sbs-battery.c
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define SBS_CHARGER_REG_SPEC_INFO 0x11
|
||||
#define SBS_CHARGER_REG_STATUS 0x13
|
||||
#define SBS_CHARGER_REG_ALARM_WARNING 0x16
|
||||
|
||||
#define SBS_CHARGER_STATUS_CHARGE_INHIBITED BIT(1)
|
||||
#define SBS_CHARGER_STATUS_RES_COLD BIT(9)
|
||||
#define SBS_CHARGER_STATUS_RES_HOT BIT(10)
|
||||
#define SBS_CHARGER_STATUS_BATTERY_PRESENT BIT(14)
|
||||
#define SBS_CHARGER_STATUS_AC_PRESENT BIT(15)
|
||||
|
||||
#define SBS_CHARGER_POLL_TIME 500
|
||||
|
||||
struct sbs_info {
|
||||
struct i2c_client *client;
|
||||
struct power_supply *power_supply;
|
||||
struct regmap *regmap;
|
||||
struct delayed_work work;
|
||||
unsigned int last_state;
|
||||
};
|
||||
|
||||
static int sbs_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct sbs_info *chip = power_supply_get_drvdata(psy);
|
||||
unsigned int reg;
|
||||
|
||||
reg = chip->last_state;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = !!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = !!(reg & SBS_CHARGER_STATUS_AC_PRESENT);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
|
||||
if (!(reg & SBS_CHARGER_STATUS_BATTERY_PRESENT))
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
else if (reg & SBS_CHARGER_STATUS_AC_PRESENT &&
|
||||
!(reg & SBS_CHARGER_STATUS_CHARGE_INHIBITED))
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
if (reg & SBS_CHARGER_STATUS_RES_COLD)
|
||||
val->intval = POWER_SUPPLY_HEALTH_COLD;
|
||||
if (reg & SBS_CHARGER_STATUS_RES_HOT)
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_check_state(struct sbs_info *chip)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, ®);
|
||||
if (!ret && reg != chip->last_state) {
|
||||
chip->last_state = reg;
|
||||
power_supply_changed(chip->power_supply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sbs_delayed_work(struct work_struct *work)
|
||||
{
|
||||
struct sbs_info *chip = container_of(work, struct sbs_info, work.work);
|
||||
|
||||
sbs_check_state(chip);
|
||||
|
||||
schedule_delayed_work(&chip->work,
|
||||
msecs_to_jiffies(SBS_CHARGER_POLL_TIME));
|
||||
}
|
||||
|
||||
static irqreturn_t sbs_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct sbs_info *chip = data;
|
||||
int ret;
|
||||
|
||||
ret = sbs_check_state(chip);
|
||||
|
||||
return ret ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static enum power_supply_property sbs_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
static bool sbs_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg < SBS_CHARGER_REG_SPEC_INFO)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sbs_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case SBS_CHARGER_REG_STATUS:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config sbs_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.max_register = SBS_CHARGER_REG_ALARM_WARNING,
|
||||
.readable_reg = sbs_readable_reg,
|
||||
.volatile_reg = sbs_volatile_reg,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE, /* since based on SMBus */
|
||||
};
|
||||
|
||||
static const struct power_supply_desc sbs_desc = {
|
||||
.name = "sbs-charger",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.properties = sbs_properties,
|
||||
.num_properties = ARRAY_SIZE(sbs_properties),
|
||||
.get_property = sbs_get_property,
|
||||
};
|
||||
|
||||
static int sbs_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct sbs_info *chip;
|
||||
int ret, val;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(struct sbs_info), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
psy_cfg.of_node = client->dev.of_node;
|
||||
psy_cfg.drv_data = chip;
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client, &sbs_regmap);
|
||||
if (IS_ERR(chip->regmap))
|
||||
return PTR_ERR(chip->regmap);
|
||||
|
||||
/*
|
||||
* Before we register, we need to make sure we can actually talk
|
||||
* to the battery.
|
||||
*/
|
||||
ret = regmap_read(chip->regmap, SBS_CHARGER_REG_STATUS, &val);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to get device status\n");
|
||||
return ret;
|
||||
}
|
||||
chip->last_state = val;
|
||||
|
||||
chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(chip->power_supply)) {
|
||||
dev_err(&client->dev, "Failed to register power supply\n");
|
||||
return PTR_ERR(chip->power_supply);
|
||||
}
|
||||
|
||||
/*
|
||||
* The sbs-charger spec doesn't impose the use of an interrupt. So in
|
||||
* the case it wasn't provided we use polling in order get the charger's
|
||||
* status.
|
||||
*/
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, sbs_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
dev_name(&client->dev), chip);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to request irq, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
|
||||
schedule_delayed_work(&chip->work,
|
||||
msecs_to_jiffies(SBS_CHARGER_POLL_TIME));
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
"%s: smart charger device registered\n", client->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sbs_info *chip = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sbs_dt_ids[] = {
|
||||
{ .compatible = "sbs,sbs-charger" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sbs_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id sbs_id[] = {
|
||||
{ "sbs-charger", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sbs_id);
|
||||
|
||||
static struct i2c_driver sbs_driver = {
|
||||
.probe = sbs_probe,
|
||||
.remove = sbs_remove,
|
||||
.id_table = sbs_id,
|
||||
.driver = {
|
||||
.name = "sbs-charger",
|
||||
.of_match_table = of_match_ptr(sbs_dt_ids),
|
||||
},
|
||||
};
|
||||
module_i2c_driver(sbs_driver);
|
||||
|
||||
MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
|
||||
MODULE_DESCRIPTION("SBS smart charger driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -35,22 +35,22 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tps65217.h>
|
||||
|
||||
#define CHARGER_STATUS_PRESENT (TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR)
|
||||
#define NUM_CHARGER_IRQS 2
|
||||
#define POLL_INTERVAL (HZ * 2)
|
||||
|
||||
struct tps65217_charger {
|
||||
struct tps65217 *tps;
|
||||
struct device *dev;
|
||||
struct power_supply *ac;
|
||||
struct power_supply *psy;
|
||||
|
||||
int ac_online;
|
||||
int prev_ac_online;
|
||||
int online;
|
||||
int prev_online;
|
||||
|
||||
struct task_struct *poll_task;
|
||||
|
||||
int irq;
|
||||
};
|
||||
|
||||
static enum power_supply_property tps65217_ac_props[] = {
|
||||
static enum power_supply_property tps65217_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
||||
@ -95,7 +95,7 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)
|
||||
int ret;
|
||||
|
||||
/* charger already enabled */
|
||||
if (charger->ac_online)
|
||||
if (charger->online)
|
||||
return 0;
|
||||
|
||||
dev_dbg(charger->dev, "%s: enable charging\n", __func__);
|
||||
@ -110,19 +110,19 @@ static int tps65217_enable_charging(struct tps65217_charger *charger)
|
||||
return ret;
|
||||
}
|
||||
|
||||
charger->ac_online = 1;
|
||||
charger->online = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps65217_ac_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
static int tps65217_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct tps65217_charger *charger = power_supply_get_drvdata(psy);
|
||||
|
||||
if (psp == POWER_SUPPLY_PROP_ONLINE) {
|
||||
val->intval = charger->ac_online;
|
||||
val->intval = charger->online;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
@ -133,7 +133,7 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)
|
||||
int ret, val;
|
||||
struct tps65217_charger *charger = dev;
|
||||
|
||||
charger->prev_ac_online = charger->ac_online;
|
||||
charger->prev_online = charger->online;
|
||||
|
||||
ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
@ -144,8 +144,8 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)
|
||||
|
||||
dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
|
||||
|
||||
/* check for AC status bit */
|
||||
if (val & TPS65217_STATUS_ACPWR) {
|
||||
/* check for charger status bit */
|
||||
if (val & CHARGER_STATUS_PRESENT) {
|
||||
ret = tps65217_enable_charging(charger);
|
||||
if (ret) {
|
||||
dev_err(charger->dev,
|
||||
@ -153,11 +153,11 @@ static irqreturn_t tps65217_charger_irq(int irq, void *dev)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
} else {
|
||||
charger->ac_online = 0;
|
||||
charger->online = 0;
|
||||
}
|
||||
|
||||
if (charger->prev_ac_online != charger->ac_online)
|
||||
power_supply_changed(charger->ac);
|
||||
if (charger->prev_online != charger->online)
|
||||
power_supply_changed(charger->psy);
|
||||
|
||||
ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
|
||||
if (ret < 0) {
|
||||
@ -188,11 +188,11 @@ static int tps65217_charger_poll_task(void *data)
|
||||
}
|
||||
|
||||
static const struct power_supply_desc tps65217_charger_desc = {
|
||||
.name = "tps65217-ac",
|
||||
.name = "tps65217-charger",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.get_property = tps65217_ac_get_property,
|
||||
.properties = tps65217_ac_props,
|
||||
.num_properties = ARRAY_SIZE(tps65217_ac_props),
|
||||
.get_property = tps65217_charger_get_property,
|
||||
.properties = tps65217_charger_props,
|
||||
.num_properties = ARRAY_SIZE(tps65217_charger_props),
|
||||
};
|
||||
|
||||
static int tps65217_charger_probe(struct platform_device *pdev)
|
||||
@ -200,8 +200,10 @@ static int tps65217_charger_probe(struct platform_device *pdev)
|
||||
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tps65217_charger *charger;
|
||||
struct power_supply_config cfg = {};
|
||||
int irq;
|
||||
struct task_struct *poll_task;
|
||||
int irq[NUM_CHARGER_IRQS];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s\n", __func__);
|
||||
|
||||
@ -216,18 +218,16 @@ static int tps65217_charger_probe(struct platform_device *pdev)
|
||||
cfg.of_node = pdev->dev.of_node;
|
||||
cfg.drv_data = charger;
|
||||
|
||||
charger->ac = devm_power_supply_register(&pdev->dev,
|
||||
&tps65217_charger_desc,
|
||||
&cfg);
|
||||
if (IS_ERR(charger->ac)) {
|
||||
charger->psy = devm_power_supply_register(&pdev->dev,
|
||||
&tps65217_charger_desc,
|
||||
&cfg);
|
||||
if (IS_ERR(charger->psy)) {
|
||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
||||
return PTR_ERR(charger->ac);
|
||||
return PTR_ERR(charger->psy);
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "AC");
|
||||
if (irq < 0)
|
||||
irq = -ENXIO;
|
||||
charger->irq = irq;
|
||||
irq[0] = platform_get_irq_byname(pdev, "USB");
|
||||
irq[1] = platform_get_irq_byname(pdev, "AC");
|
||||
|
||||
ret = tps65217_config_charger(charger);
|
||||
if (ret < 0) {
|
||||
@ -235,29 +235,36 @@ static int tps65217_charger_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq != -ENXIO) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
/* Create a polling thread if an interrupt is invalid */
|
||||
if (irq[0] < 0 || irq[1] < 0) {
|
||||
poll_task = kthread_run(tps65217_charger_poll_task,
|
||||
charger, "ktps65217charger");
|
||||
if (IS_ERR(poll_task)) {
|
||||
ret = PTR_ERR(poll_task);
|
||||
dev_err(charger->dev,
|
||||
"Unable to run kthread err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
charger->poll_task = poll_task;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create IRQ threads for charger interrupts */
|
||||
for (i = 0; i < NUM_CHARGER_IRQS; i++) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
|
||||
tps65217_charger_irq,
|
||||
0, "tps65217-charger",
|
||||
charger);
|
||||
if (ret) {
|
||||
dev_err(charger->dev,
|
||||
"Unable to register irq %d err %d\n", irq,
|
||||
"Unable to register irq %d err %d\n", irq[i],
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check current state */
|
||||
tps65217_charger_irq(irq, charger);
|
||||
} else {
|
||||
charger->poll_task = kthread_run(tps65217_charger_poll_task,
|
||||
charger, "ktps65217charger");
|
||||
if (IS_ERR(charger->poll_task)) {
|
||||
ret = PTR_ERR(charger->poll_task);
|
||||
dev_err(charger->dev,
|
||||
"Unable to run kthread err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
tps65217_charger_irq(-1, charger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -267,7 +274,7 @@ static int tps65217_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65217_charger *charger = platform_get_drvdata(pdev);
|
||||
|
||||
if (charger->irq == -ENXIO)
|
||||
if (charger->poll_task)
|
||||
kthread_stop(charger->poll_task);
|
||||
|
||||
return 0;
|
||||
|
@ -175,11 +175,6 @@ static int wm97xx_bat_probe(struct platform_device *dev)
|
||||
if (dev->id != -1)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&dev->dev, "No platform_data supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->charge_gpio)) {
|
||||
ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
|
||||
if (ret)
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum {
|
||||
enum axp20x_variants {
|
||||
AXP152_ID = 0,
|
||||
AXP202_ID,
|
||||
AXP209_ID,
|
||||
@ -532,35 +532,6 @@ struct axp20x_dev {
|
||||
const struct regmap_irq_chip *regmap_irq_chip;
|
||||
};
|
||||
|
||||
#define BATTID_LEN 64
|
||||
#define OCV_CURVE_SIZE 32
|
||||
#define MAX_THERM_CURVE_SIZE 25
|
||||
#define PD_DEF_MIN_TEMP 0
|
||||
#define PD_DEF_MAX_TEMP 55
|
||||
|
||||
struct axp20x_fg_pdata {
|
||||
char battid[BATTID_LEN + 1];
|
||||
int design_cap;
|
||||
int min_volt;
|
||||
int max_volt;
|
||||
int max_temp;
|
||||
int min_temp;
|
||||
int cap1;
|
||||
int cap0;
|
||||
int rdc1;
|
||||
int rdc0;
|
||||
int ocv_curve[OCV_CURVE_SIZE];
|
||||
int tcsz;
|
||||
int thermistor_curve[MAX_THERM_CURVE_SIZE][2];
|
||||
};
|
||||
|
||||
struct axp20x_chrg_pdata {
|
||||
int max_cc;
|
||||
int max_cv;
|
||||
int def_cc;
|
||||
int def_cv;
|
||||
};
|
||||
|
||||
struct axp288_extcon_pdata {
|
||||
/* GPIO pin control to switch D+/D- lines b/w PMIC and SOC */
|
||||
struct gpio_desc *gpio_mux_cntl;
|
||||
|
@ -4,8 +4,16 @@
|
||||
enum bq27xxx_chip {
|
||||
BQ27000 = 1, /* bq27000, bq27200 */
|
||||
BQ27010, /* bq27010, bq27210 */
|
||||
BQ27500, /* bq27500 */
|
||||
BQ27510, /* bq27510, bq27520 */
|
||||
BQ2750X, /* bq27500 deprecated alias */
|
||||
BQ2751X, /* bq27510, bq27520 deprecated alias */
|
||||
BQ27500, /* bq27500/1 */
|
||||
BQ27510G1, /* bq27510G1 */
|
||||
BQ27510G2, /* bq27510G2 */
|
||||
BQ27510G3, /* bq27510G3 */
|
||||
BQ27520G1, /* bq27520G1 */
|
||||
BQ27520G2, /* bq27520G2 */
|
||||
BQ27520G3, /* bq27520G3 */
|
||||
BQ27520G4, /* bq27520G4 */
|
||||
BQ27530, /* bq27530, bq27531 */
|
||||
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
|
||||
BQ27545, /* bq27545 */
|
||||
|
@ -81,6 +81,7 @@
|
||||
#define AT91_DDRSDRC_LPCB_POWER_DOWN 2
|
||||
#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3
|
||||
#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */
|
||||
#define AT91_DDRSDRC_LPDDR2_PWOFF (1 << 3) /* LPDDR Power Off */
|
||||
#define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */
|
||||
#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */
|
||||
#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */
|
||||
@ -96,7 +97,9 @@
|
||||
#define AT91_DDRSDRC_MD_SDR 0
|
||||
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
|
||||
#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3
|
||||
#define AT91_DDRSDRC_MD_LPDDR3 5
|
||||
#define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */
|
||||
#define AT91_DDRSDRC_MD_LPDDR2 7
|
||||
#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */
|
||||
#define AT91_DDRSDRC_DBW_32BITS (0 << 4)
|
||||
#define AT91_DDRSDRC_DBW_16BITS (1 << 4)
|
||||
|
Loading…
Reference in New Issue
Block a user